What is your goal for the lll?

Captain Napalm spc@armigeron.com
Mon, 29 Apr 1996 06:06:04 -0400 (EDT)


A long long time ago on a network far far away, Nathan Hawkins thus said:
> On Sat, 27 Apr 1996, Captain Napalm wrote:
> > A long long time ago on a network far far away, Nathan Hawkins thus said:
> > > 
> As for your language, I screwed up the shell archive somehow, and haven't 
> found the time to fix it, so I haven't had a chance to look at it. :-(
> If you have an ftp location, or can get it to gzip well, could you try to 
> get it to me again?
> 
  It'll have to wait a bit.  The machine the archive is on is down for the
weekend (stupid admin - a planned power outage for half an hour early
Saturday and he takes the machine down for the whole weekend, like ther
aren't people that USE the machine.  But I digress ... ) and I'm pretty much
still in crunch mode til Tuesday noon EST (approximately).

> >   That, and while I like Forth (and the system started out with Forth in
> > mind), I don't like Forth at the same time.
> > 
> >   Actually, I like Forth on a theoretical level, not on the actual
> > implimentation level, so I have no desire to really keep compatibility with
> > Forth (79, 83, Fig or ANS).
> 
> I like some implementations better than others... I particularly _don't_ 
> like any of the ones I've tried for Linux. 

  Not even PFE?  That's a fairly nice ANS Forth system.  A bit big, but I
like it (since I did some work on the SGI port 8-)

> Mine were all slightly 
> incompatible, things like doing the "user" variables as TO variables, 
> compiling input (like in the thread currently going on in c.l.f), stuff 
> like that. I also like things to be as simple as possible, which some 
> systems don't...
> 
  Well, the next version of VIth will probably be in compile mode all the
time (more on that later).

> >   An idea might be to forgo an ANS version and go with a Forth-like
> > language, then add an ANS layer to it (like QNX has added a POSIX layer). 
> > My current language design (which I haven't had much time to work on lately)
> > has a vastly new structure beneath it, and adding an ANS layer to this
> > shouldn't be a problem.
> 
> That would be fine, as long as it's possible to do an ANS layer in a more 
> or less transparent way. (That is, don't implement in a way that's 
> totally incompatible.)
> 
  Well, I was playing around with ideas for the next release of VIth.  The
structure is turning out to be quite a bit like LISP (in a way).  It cleans
up a lot of the "special cases" that I have right now, and the parser
(actually, the interpreter/compiler) suddently became easier to write.

  I don't have the notes files I created (it's at home, I'm at work right
now).  Some notes:  Anything between '' or "" is taken as a string and
dumped into the stack (actually, a reference).  { starts a "list" (it's what
it is, nothing to do with LISP lists) and } finishes a list and puts it on
the stack.  Memory is 'tagged' with information.  Okay, so this doubles the
amount of memory most simple types take, but it does simplify things and
makes for safe-typing and possible GC.  So, an integer may take up to eight
bytes, four for the actual value, and another four for other information
(type, size, etc).  It's something I can live with, and can be taken out
when the code is "compiled" into machine code, as apposed to interpreted. 
The ` character returns the execution token of the following word (much like
' in ANS Forth).

  Anyway, some example neo-VIth code:


'(lastdef)'	xt var'		( var' takes a string, makes a int )
'state'		int var'	( variable with that name	)
'defimm'	{ dup setimmediate define }

0 state !

'{{'	{ ` { exec } defimm
	// setimmediate ( xt -- ) takes XT and makes it immediate 
	// define ( string xt -- ) adds word to current vocabulary

'}}'	{ ` } exec } defimm
'}}`'	{ ` }` exec } defimm

	// } closes off a list and leaves a selfexec list
	// }` closes off a list and leaves a list (which can be execed)

':'	{ stdtoken ( read in next space delim word ) {{ 1 state +! } defimm
';'	{ }} dup (lastvar) ! define -1 state +! } defimm
'immediate'	{ (lastvar) @ setimmediate } defimm

( now we have a more or less traditional way of writing code )

: ,	compile ;
: if	` ifthen {{ ; immediate
: else	}} swap drop ` ifelse {{ ; immediate
: then	}} swap , ; immediate

  Note that the following:

	cond? if do.something then

  is actually "compiled" as:

	cond? { do.something} ifthen

  and

	cond? if do.a else do.b then

  becomes:

	cond? { do.a } { do.b } ifelse

  Also, since I plan to always be in compile mode, one can then use 'if
then' in "immediate" mode (start with an implicit '{' having been done and
set state to 1.  For each {, increment state.  For each } decrement it.  If
the enter key is pressed and state is 1, do an implicit }, and execute the
top of stack) such as:

	3 4 = if "nope they're not equal" else "yup they are" then print

  One other thing I'm planning on doing (for something really radical here)
is to make everything self-executing.  What I mean by that is, if everything
is typed, then when anything is executed, the system can determine what to
do (or rather, let the object decide what to do).  In a tradional Forth
system, the following:

	: 3x+5	3 * 5 + ;

  Internally, looks like:

	[ lit ] [ 3 ] [ * ] [ lit ] [ 5 ] [ + ] [ next ]

  (stuff in [] represent cells).  In neo-VIth, it'll be:

	[ int:3] [ word:*] [ int:5 ] [ word:+ ] [ word:next ]

  Actually, using the notation I developed, it would be:

	[ int 	]	[ 3			]
	[ word	]	[ *			]
	[ int	]	[ 5			]
	[ word	]	[ +			]
	[ word	]	[ next			]

  	^type info	^ contents

  So the phrase

	3 4 = if "nope" else "yup" then print

  would actually be:

	{ 3 4 = { "nope" } { "yup" } ifelse print }

  And be stored as (using a slight modification)

[0]	[ 3 4 = [1]` [2]` ifelse print ]
[1]	[ "nope" ]
[2]	[ "yup"	 ]

(there's an implicit end of list marker)

[0]	[ int	]	[ 3					]
	[ int	]	[ 4					]
	[ word	]	[ = 					]
	[ list	]	[ [1]					]
	[ list	]	[ [2]					]
	[ word	]	[ ifelse				]
	[ word	]	[ print					]

[1]	[ str	]	[ "nope"				]

[2]	[ str	]	[ "yup"					]

(usually, when a type is executed, it's value is pushed onto the stack.  An
exeception is 'word' which executes it's value).

  Another example:

	?a if a begin c? while doc repeat b c else x y z then l m o 

	{ ?a { a { c? exit? doc again } b c }` { x y z }` ifelse l m o }

(you can see the LISPisms here 8-)

[0]	[ ?a [1]` [2]` ifelse l m o ]
[1]	[ a [3] b c ]
[2]	[ x y z ]
[3]	[ c? exit? doc again ]

[0]	[ word	]	[ ?a					]
	[ list	]	[ [1]					]
	[ list	]	[ [2]					]
	[ word	]	[ ifelse				]
	[ word	]	[ l					]
	[ word	]	[ m					]
	[ word	]	[ o					]

[1]	[ word	]	[ a					]
	[ word	]	[ [3]					]
	[ word	]	[ b					]
	[ word	]	[ c					]

[2]	[ word	]	[ x					]
	[ word	]	[ y					]
	[ word	]	[ z					]

[3]	[ word	]	[ c?					]
	[ word	]	[ exit? ( exits list conditionally )	]
	[ word	]	[ doc					]
	[ word	]	[ again ( re-executes list )		]

  Like I said, a type 'list' will leave a list on the stack, while a type
'word' is executed.  A 'list' can be executed by applying the 'exec' word to
it.

  But wait, I haven't gotten to the really radical stuff yet.  Since all
"memory locations" are typed, then all "memory locations" can be executed,
which includes the stack!  So, if we make the stack grow from low address to
high addresses (forward in memory) instead of the typical backwards growth
(and it doesn't matter which way a stack grows as long as you're consistant)
then 'compile' becomes a 'push'.  So you can create words on the stack, and
when done, they're gone (automatic GC is a nice feature of a stack).

  This does have some strange side effects, and with the right operators,
one can do the following pathological code:

	sp ` dup swap exec

sp returns the current stack pointer.
` dup returns the XT of dup.
exec takes an XT (and basically, any memory location is an XT) and runs it.

  Now, that might not be a good thing, but I find it neat.  And since all
memory is typed, you can determine what each memory location is.

  Now, one can always strip out type information in code that is "static"
(ie - all types are known at all points and is deterministic) to avoid the
overhead of typechecking.

> >   Might want to take a look at Java then.  I've noticed (since I've been
> > working with nothing BUT Java for the past two months) that only those parts
> > of an applet are transferred that are actually used (almost like demand
> > paging).  But once loaded, they (I assume) stay loaded.
> > 
> >   -spc (Not to say that I like Java any more than Forth - if anything, I'd
> > 	rather be doing Forth ... )
> 
> I looked very briefly, but wasn't keen on it. Too much like C++, and I 
> try to avoid that. I've even been getting sick of C, lately.
> 
  It's a cleaner language than C++ (in fact, the language itself is small,
consice and clean.  The base classes are another thing (ick)).

  Woah.  I see I probably went into quite a bit of detail.  Hopefully some
of what I'm trying to do came out, and didn't horribly confuse anyone.

  -spc (Probably rambled on too much ... )