Bart <
bc@freeuk.com> wrote:
On 07/09/2024 02:44, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 06/09/2024 11:19, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
(You can balance it out by by requiring ASSIGN(&A, &B)!)
This would not work in general, as I wrote it, the following are
valid:
assign(&a, 42)
assign(&a, a + 1)
but the second argument has no address, so your variant would not
work.
I believe that C's compound literals can give a reference to a+1:
#include <stdio.h>
void assign(int* lhs, int* rhs) {
*lhs = *rhs;
}
int main(void) {
int a=20;
assign(&a, &(int){a+1});
printf("a = %d\n", a);
}
The output from this is 21.
Yes, that would work. But you use here complex language feature,
rather unattractive if one want to use assign as a basic
construct.
You can use stack machines to get reasonably simple definition of
semantics. But still slightly more complex than what I outlined
above. And code for stack machines is unpleasent to optimize.
In a compiler for a language where official semantics is stack
based the first things that compiler does is to track few items
on top of the stack and match them to sequences like
push a
push b
call op
Once sequence is matched it is replaced by corresonding operation
on variables. If this matching works well you get conventional
(non-stack) intermediate representaion and reasonably good code.
If matching fails, the resulting object code wastes time on stack
operations.
(My stack IL is different. The stack is a compile-time stack only, and
code is scanned linearly during code-generation. Roughly, the 'stack'
corresponds to the machine's register-file, although in practice
register allocation is ad-hoc.
OK, that is another way to work around slowness of the stack. But
you get into trouble when you have more items on the stack than
number of available registers.
Best Forth compilers
have sophisticated code to track stack use and replace it by
use of registers. Other Forth compilers just accept slowness.
Then you no longer have a language which can be implemented in a few KB.
You might as well use a real with with proper data types, and not have
the stack exposed in the language. Forth code can be very cryptic
because of that.
First, it is not my goal to advocate for Forth use. But I think
it is interesting to understand why things were done in the past.
Concerning exposed stack, Pop11 has exposed stack. 5 line
executive summary could be the same as of Forth. But language
is feels quite different. Pop11 uses classic infix syntax, and
variable access puts "value" on the stack. I put "value" in
scare qoutes because most of Pop11 data items are composite
and live in memory. They are accessed using their address and
that is what is put on the stack. But semantically you deal
with values and addresses are normally hidden from users. If you
wish you can ignore the stack. Presence of the stack is visible
in examples like below:
: 2*3 =>
** 6
: 2, 3, *() =>
** 6
the first line is infix form, '=>' oprator prints what is on
the stack (but you cat treat it as "print current result").
In the second line two numbers are pushed on the stack and
then there is call to multiplication routine. Parser knows
that '*' is an operator, but since there are no argument
'*' is treated as ordinary identifer and as result you
get multiplication routine. Like in other languages parentheses
mean function call. Up to now this may look just as some
weirdness with no purpose. But there are advantages. One
is that Pop11 functions can return multiple values, they just
put as many values as needed on the stack. Second, one can
write functions which take variable number of arguments.
And one can use say a loop to put varible number of arguments
on the stack and then call a routine expecting variable
number of arguments. In fact, there is common Pop11 idiom
to handle aggregas: 'explode' puts all members of the aggregate
on the stack. There are also constructor function which build
aggregates from values on the stack.
Coming back to Forth, you can easily add infix syntax to Forth
but Forth users somewhat dislike idea of using infix for most
of programming. My personal opinion is that Fort was good
around 1980. At that time there was quite simple implementation,
language offered interactive developement and some powerful
feature and there were interesting compromise between speed
and size. Namely, Forh code tended to be smaller than
machine code and deliver speed lower than machine code but
better than some other alternatives. Now, interactive developement
is done on bigger machines, so small size is not so important.
Traditional Forh uses so called threaded code, which has machine
word sized units. With 16-bit words that leads to relatively
compact code. With 32-bit words you double code size. And
on 64-bit machines this is quite wasteful.
What started the subthread was the question of which HLL goes between
ASM and C (since someone suggested that C was mid-level).
Well, for me important question is how much work is due to tools
(basically overhead) and how much deals with problem domain.
Since computers are now much heaper compared to human work
there is desire to reduce tool overhead as much as possible.
This favours higher level languages, so probably most recently
created languages is at higher level than C. However, in
sixties and seventies there were pack of so called algorithmic
languages or somewhat more specifically Algol family. I would
say that C is close to the middle of this pack. As a devils
advocate let me compare typical implementation of early Pascal
with C modern C. In C variables, including aggregates can be
initialized at declarartion time, early Pascal did not allow
this, so one had to initalize variables by code. In C function
definition gives argument names and types, even if there is
earlier prototype. In early Pascal, if you gave froward
declaration (equivalent of C prototype), you _had_ to omit
function parameters (and their types). That significantly
decreased readabilty of such functions. In early Pascal
one had to declare all local variables at the start of
a fucntion. C has block structure, so one can limit variable
scope to area where variable makes sense. More importat, one
can declare variable when there is sensible initial value,
which significantly decreases chance of missing/wrong
initalization. Many early Pascal implementations did not
have conformant arrays (present in original Wirth Pascal).
That meant that all arrays had to be of size known at compile
time. From C99 C has variably modified types, which allow
writing functions accepting arrays of varying sizes allocated
elsewere. Let me also mention C preprocessor: it allows programmer
to effectively define simple language extention, so that
language better fits to problem domain. This in not available
in standard Pascal. Of course, C has buch of low level features as
casts and pointer arithmetic. However, with variably modified
types pointer arthmetic is no longer needed and casts are system
programming feature not needed for normal programs. So if presence
of those bothers you the simple solution is not to use them
(of course C standard defines array access in terms of pointer
artihtmetic, but one can avoid explicit pointer artimetic and
allow only array access in the sources). Early Pascal has a
bunch of features not present in C, but one can reasonably consider
modern C to be higher level than early Pascal. And _a lot_
of early languages were at lower level than Pascal.
People suggested ones like BLISS and Forth.
I remarked that a proper HLL would let you write just A to either read
the value of variable A, or write to it. Eg. A = A, without special
operators to dereference A's address.
You are looking at superficial things. Forth is extensible, fetch
appropriate extention from the net and you can write expressions
in infix form using normal syntax. IIUC Bliss has powerful
preprocessor, I am not sure if it is powerful enough to transform
expression without dereferences into ones with dereferences, but
even if it can not do that extensibility allows to write clearer
code closer to problem domain.
I am not going to write substantial programs in Bliss or Forth
but I have no double they are HLL-s.
-- Waldek Hebisch