Mon Jun 25 14:37:20 CEST 2007
literal stack + compilation stack
the important thing about stacks is that you need two of them, i once
read. which seems to be the case. currently i'm trying to figure out
what should go to what by default.
the idea of the 'literal stack' is simply to be able to do some
computation at compile time. a nice feature here is that a lot of
operations become more natural. for example:
1 2 +
is really just 3. and this is a mandatory optimization in
badnop. something you can rely on as a feature. standard forth would
make this explicit
[ 1 2 + ]L
the reason i dont use the above is that my meta language is not
forth. it's CAT. more importantly, CAT is much more powerful than the
simple 8 bit forth is.
so, the idea goes:
- mandatory literal optimization (compile time evaluation)
- forth extended with 'ghost' types
the ghost types are things that make no sense for the microcontroller,
but when they are combined with other ghost types, result in things
that do make sense. the most obvious one is assembler labels:
will compile code that loads the (symbolic) address of foo. if this is
followed by a macro that consumes it, the whole can be reduced to code
that does have a meaning on the microcontroller.
i'm not 100% convinced this is a good idea (not being explicit), but
it does feel like one. what i'm looking for is to give it a decent
meaning. and to find out when to use the literal stack, and when to
use the compilation stack.
another thorn is the way the literal stack is implemented, but that
can be fixed later. right now i need to get the semantics right.
i'm not asking the right question..
what's the real problem here? the target chip has a clear separation
of ROM and RAM. this is both convenient (code is persistent), and not
(they need to be treated differently).
what i'd like to do is to make a source file correspond to only
ROM. standard forth doesn't do that: loading a file both writes code
and initializes data. i guess this is the main reason why things are
different for me:
harvard: ram initialization (run-time code) and meta compilation
(compile-time code) are strictly separate.
von-neumann: both can be done at the same time (program load time),
and blurr together.
so what does this have to do with the literal stack?
- the meta language is not forth
- i'm trying to disguise this
basicly, i'd like to not think about this thing being a
cross-compiler, and act as if everything runs on the target. one way
of doing that, is to require compile time evaluation whenever it is
possible. as a result, the simple recursive macro system, which does
not refer to the real meta language directly, becomes more powerful:
required partial evaluation gives it some run-time power, instead of
merely being passive concatenation of code.
so the real question is:
how to simplify the target language such that no explicit reference
to the meta language is ever necessary, and all macros have a
the way that seems most natural to me is:
- partial evaluation is the default: act as if everything is done at
run time (like "1 2 +"), but write the macros such that they perform
compile time compilation + raise an error when higher level things
can't be resolved at compile time.
- some constructs use the COMPILATION STACK referred to as 'c'. this
is mainly intended for code blocks, and serves a bit the role of the
this also gives the solution for parsing words: their default
semantics is to map something to a literal compiler.
a common problem i encountered is a macro which has 2 references to
the same name. this is now easily solved using the compilation stack.
so the key is really in the words '>c' and 'c>'