So is that list sensible???
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On the other hand, it can’t be, because it includes Excel. What a laugh
...
Perhaps you could explain humor to the rest of us.
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
Am 24.08.2024 um 00:03 schrieb John Forkosh:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
Meanwhile real C++ code has several times more boilerplate than C. HTF
you can even discern your actual program amidst all that crap is beyond me.
There /are/ proper higher level languages than both C and C++. You can
use one to help develop a working application, then porting that part to
C is a quicker, simpler and safer process.
John Forkosh wrote:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
As far as I see, the article is about what people put on their CV's -
not what they /should/ put, or what potential employers want.
Basically, it is pretty useless - you could use it to argue that people
think (rightly or wrongly) that C skills are useful for getting a job,
or that people with C skills are regularly out of a job and needing to
apply for a new one.
And you can also expect that the people behind the article don't know
the difference between C, C++ and C#.
On 24/08/2024 19:11, Bonita Montero wrote:
Am 24.08.2024 um 00:03 schrieb John Forkosh:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
Meanwhile real C++ code has several times more boilerplate than C. HTF
you can even discern your actual program amidst all that crap is beyond me.
There /are/ proper higher level languages than both C and C++. You can
use one to help develop a working application, then porting that part to
C is a quicker, simpler and safer process.
David Brown <david.brown@hesbynett.no> wrote:
John Forkosh wrote:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
As far as I see, the article is about what people put on their CV's
- not what they /should/ put, or what potential employers want.
Basically, it is pretty useless - you could use it to argue that
people think (rightly or wrongly) that C skills are useful for
getting a job, or that people with C skills are regularly out of a
job and needing to apply for a new one.
And you can also expect that the people behind the article don't
know the difference between C, C++ and C#.
Yeah, I guess "C is #3" was just unlikely wishful thinking
on my part (I'm now hoping my lottery ticket is a winner).
So, is there any reasonably reliable such "Top 10" list?
If so, where? If not, where would C fall on it if it did
exist? (I'd probably guess C>10, so make that a "Top 100"
list, as needed.)
Bart <bc@freeuk.com> wrote:...
I recall C as originally characterized as a "portable assembly language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by trying
to evaluate its merits/demerits vis-a-vis higher-level languages.
Consider it with respect to its own objectives, instead.
Yeah, I guess "C is #3" was just unlikely wishful thinking
on my part (I'm now hoping my lottery ticket is a winner).
So, is there any reasonably reliable such "Top 10" list?
If so, where? If not, where would C fall on it if it did
exist? (I'd probably guess C>10, so make that a "Top 100"
list, as needed.)
On 8/25/24 08:18, John Forkosh wrote:
Bart <bc@freeuk.com> wrote:...
I recall C as originally characterized as a "portable assembly language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by trying
to evaluate its merits/demerits vis-a-vis higher-level languages.
Consider it with respect to its own objectives, instead.
C has been mischaracterized as a "portable assembly language", but that
has never been an accurate characterization. It has, from the very
beginning, been defined by the behavior that is supposed to result from translating and executing the C code, not the assembly language that's supposed to be produced by the translation process.
C is a high level language. It is a very low-level high-level language,
but it's not in any sense an assembler.
On 8/25/24 08:09, John Forkosh wrote:
...
Yeah, I guess "C is #3" was just unlikely wishful thinking
on my part (I'm now hoping my lottery ticket is a winner).
So, is there any reasonably reliable such "Top 10" list?
If so, where? If not, where would C fall on it if it did
exist? (I'd probably guess C>10, so make that a "Top 100"
list, as needed.)
If you're just looking for a Top 10 list, and don't care "Top 10
what?", then the Tiobe index <https://www.tiobe.com/tiobe-index/>
might satisfy. There's lots of controversy about their methodology,
despite which I think it is the least controversial attempt to rank
all computer languages.
James Kuyper wrote:
On 8/25/24 08:18, John Forkosh wrote:
Bart <bc@freeuk.com> wrote:...
I recall C as originally characterized as a "portable assembly
language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by trying
to evaluate its merits/demerits vis-a-vis higher-level languages.
Consider it with respect to its own objectives, instead.
C has been mischaracterized as a "portable assembly language", but that
has never been an accurate characterization. It has, from the very
beginning, been defined by the behavior that is supposed to result from
translating and executing the C code, not the assembly language that's
supposed to be produced by the translation process.
C is a high level language. It is a very low-level high-level language,
but it's not in any sense an assembler.
c is mid level language - i mean it has more sense to call c that way
than call it low level or high level
So what language goes between Assembly and C?
On 25/08/2024 15:55, fir wrote:
James Kuyper wrote:
On 8/25/24 08:18, John Forkosh wrote:
Bart <bc@freeuk.com> wrote:...
I recall C as originally characterized as a "portable assembly
language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by
trying to evaluate its merits/demerits vis-a-vis higher-level
languages. Consider it with respect to its own objectives,
instead.
C has been mischaracterized as a "portable assembly language", but
that has never been an accurate characterization. It has, from the
very beginning, been defined by the behavior that is supposed to
result from translating and executing the C code, not the assembly
language that's supposed to be produced by the translation process.
C is a high level language. It is a very low-level high-level
language, but it's not in any sense an assembler.
c is mid level language - i mean it has more sense to call c that
way than call it low level or high level
So what language goes between Assembly and C?
There aren't many!
So it's reasonable to consider C as being at the
lowest level of HLLs.
Putting C at mid-level would make for a very cramped space above it
as 99% of languages would have to fit in there.
Am 25.08.2024 um 16:50 schrieb James Kuyper:
C is a high level language. It is a very low-level high-level
language, but it's not in any sense an assembler.
C is almost the lowest-level of all high level lanugages.
I don't know any further language which lacks nearly every
abstraction.
On 8/25/24 08:18, John Forkosh wrote:
...
I recall C as originally characterized as a "portable assembly language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by trying
to evaluate its merits/demerits vis-a-vis higher-level languages.
Consider it with respect to its own objectives, instead.
C has been mischaracterized as a "portable assembly language", but that
has never been an accurate characterization. It has, from the very
beginning, been defined by the behavior that is supposed to result from translating and executing the C code, not the assembly language that's supposed to be produced by the translation process.
C is a high level language. It is a very low-level high-level language,
but it's not in any sense an assembler.
[...]
C is almost the lowest-level of all high level lanugages.
I don't know any further language which lacks nearly every abstraction.
Bart <bc@freeuk.com> wrote:
On 24/08/2024 19:11, Bonita Montero wrote:
Am 24.08.2024 um 00:03 schrieb John Forkosh:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
Meanwhile real C++ code has several times more boilerplate than C. HTF
you can even discern your actual program amidst all that crap is beyond me. >>
There /are/ proper higher level languages than both C and C++. You can
use one to help develop a working application, then porting that part to
C is a quicker, simpler and safer process.
I recall C as originally characterized as a "portable assembly language",
as opposed to a "higher level language". And I'd agree with that
assessment,
whereby I think you're barking up the wrong tree by trying
to evaluate its merits/demerits vis-a-vis higher-level languages.
Consider it with respect to its own objectives, instead.
On 24/08/2024 19:11, Bonita Montero wrote:
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
[...]
David Brown <david.brown@hesbynett.no> wrote:
John Forkosh wrote:
I came across
https://www.fastcompany.com/91169318/
where I was quite surprised, and very happily so,
to see C listed as #3 on its list of
"Top 10 most common hard skills listed in 2023"
(scroll about halfway down for that list). Moreover,
C++ doesn't even make it anywhere in that top-10 list.
So is that list sensible??? I'd personally be delighted
if so, but I'm suspicious it may just be wishful thinking
on my part, and some kind of goofiness on the list's author.
As far as I see, the article is about what people put on their CV's -
not what they /should/ put, or what potential employers want.
Basically, it is pretty useless - you could use it to argue that people
think (rightly or wrongly) that C skills are useful for getting a job,
or that people with C skills are regularly out of a job and needing to
apply for a new one.
And you can also expect that the people behind the article don't know
the difference between C, C++ and C#.
Yeah, I guess "C is #3" was just unlikely wishful thinking
on my part (I'm now hoping my lottery ticket is a winner).
So, is there any reasonably reliable such "Top 10" list?
If so, where? If not, where would C fall on it if it did
exist? (I'd probably guess C>10, so make that a "Top 100"
list, as needed.)
On 24.08.2024 20:27, Bart wrote:
On 24/08/2024 19:11, Bonita Montero wrote:
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
It's true that C++ decided to inherit unsafe C designs as C being
sort of its base. But a sophisticated programmer would knowingly
avoid the unsafe parts and use the existing safer C++ constructs.
Only that a language allows that you *can* write bad code doesn't
mean you cannot avoid the problems. Of course it would have been
(IMO) better if the unsafe parts were replaced or left out, but
there were portability consideration in C++'s design.
Janis
[...]
On Sun, 25 Aug 2024 16:30:17 +0100
Bart <bc@freeuk.com> wrote:
On 25/08/2024 15:55, fir wrote:
James Kuyper wrote:
On 8/25/24 08:18, John Forkosh wrote:
Bart <bc@freeuk.com> wrote:...
I recall C as originally characterized as a "portable assembly
language",
as opposed to a "higher level language". And I'd agree with that
assessment, whereby I think you're barking up the wrong tree by
trying to evaluate its merits/demerits vis-a-vis higher-level
languages. Consider it with respect to its own objectives,
instead.
C has been mischaracterized as a "portable assembly language", but
that has never been an accurate characterization. It has, from the
very beginning, been defined by the behavior that is supposed to
result from translating and executing the C code, not the assembly
language that's supposed to be produced by the translation process.
C is a high level language. It is a very low-level high-level
language, but it's not in any sense an assembler.
c is mid level language - i mean it has more sense to call c that
way than call it low level or high level
So what language goes between Assembly and C?
Popular today? Not many. In the past? PL/M, BLISS. Although the former
is at almost the same level as C.
There aren't many!
Because C is seen as good enough.
So it's reasonable to consider C as being at the
lowest level of HLLs.
Putting C at mid-level would make for a very cramped space above it
as 99% of languages would have to fit in there.
Why is it a problem?
On 8/25/24 17:30, Bart wrote:
So what language goes between Assembly and C?
Forth ?
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
OOP, functional programming, generic programming, exceptions.
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
OOP, functional programming, generic programming, exceptions.
On Sun, 25 Aug 2024 18:36:46 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 24.08.2024 20:27, Bart wrote:
On 24/08/2024 19:11, Bonita Montero wrote:
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
It's true that C++ decided to inherit unsafe C designs as C being
sort of its base. But a sophisticated programmer would knowingly
avoid the unsafe parts and use the existing safer C++ constructs.
Only that a language allows that you *can* write bad code doesn't
mean you cannot avoid the problems. Of course it would have been
(IMO) better if the unsafe parts were replaced or left out, but
there were portability consideration in C++'s design.
[...]
Safe HLLs without mandatory automatic memory management
tend to fall
into two categories:
1. Those that already failed to become popular
2. Those for which it will happen soon
That despite at least one language in the 1st category being
pretty well designed, if more than a little over-engineered.
Safe HLLs without mandatory automatic memory management tend to fall
into two categories:
1. Those that already failed to become popular
2. Those for which it will happen soon
That despite at least one language in the 1st category
being pretty well designed, if more than a little over-engineered.
I don't know any further language which lacks nearly every abstraction.
On Sun, 25 Aug 2024 20:12:47 +0200 Bonita Montero
<Bonita.Montero@gmail.com> wrote:
Am 25.08.2024 um 18:28 schrieb Michael S:I don't see why any of those are abstractions.
Define "abstraction".
OOP, functional programming, generic programming, exceptions.
c is mid level language ...
So what language goes between Assembly and C?
There aren't many!
Michael S <already5chosen@yahoo.com> writes:
On Sun, 25 Aug 2024 18:36:46 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 24.08.2024 20:27, Bart wrote:
On 24/08/2024 19:11, Bonita Montero wrote:
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a
magnitude less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but
can still have most of the same problems as C.
It's true that C++ decided to inherit unsafe C designs as C being
sort of its base. But a sophisticated programmer would knowingly
avoid the unsafe parts and use the existing safer C++ constructs.
Only that a language allows that you *can* write bad code doesn't
mean you cannot avoid the problems. Of course it would have been
(IMO) better if the unsafe parts were replaced or left out, but
there were portability consideration in C++'s design.
[...]
Safe HLLs without mandatory automatic memory management
I'm not sure what you mean by this description. Do you mean
languages that are otherwise unsafe but have a safe subset?
If not that then please elaborate.
What are some examples of
"safe HLLs without mandatory automatic memory management"?
tend to fall
into two categories:
1. Those that already failed to become popular
2. Those for which it will happen soon
It's been amusing reading a discussion of which languages are or are
not high level, without anyone offering a definition of what the
term means. Wikipedia says, roughly, that a high-level language is
one that doesn't provide machine-level access (and IMO that is a
reasonable characterization).
Of course no distinction along these
lines is black and white - almost all languages have a loophole or
two - but I expect there is general agreement about which languages
clearly fail that test. In particular, any language that offers
easy access to raw memory addresses (and both C and C++ certainly
do), is not a high-level language in the Wikipedia sense.
Second amusement: using the term popular without giving any
kind of a metric that measures popularity.
Third amusement: any language that has not yet become popular
has already failed to become popular.
That despite at least one language in the 1st category being
pretty well designed, if more than a little over-engineered.
Please, don't keep us in suspense. To what language do you refer?
On Sun, 25 Aug 2024 16:30:17 +0100, Bart wrote:
So what language goes between Assembly and C?
There aren't many!
Quite a few, actually. Elsewhere I mentioned B (the precursor of C), BCPL
and BLISS.
BLISS is a rather strange language. For something supposedly low level than C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if
you declare a variable A, then you can access A's value just by writing A; its address is automatically dereferenced.
It's been amusing reading a discussion of which languages are or are
not high level, without anyone offering a definition of what the
term means.
Wikipedia says, roughly, that a high-level language is
one that doesn't provide machine-level access (and IMO that is a
reasonable characterization).
Of course no distinction along these
lines is black and white - almost all languages have a loophole or
two - but I expect there is general agreement about which languages
clearly fail that test.
In particular, any language that offers
easy access to raw memory addresses (and both C and C++ certainly
do), is not a high-level language in the Wikipedia sense.
Third amusement: any language that has not yet become popular
has already failed to become popular.
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly low level than >> C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if
you declare a variable A, then you can access A's value just by writing A; >> its address is automatically dereferenced.
Not always. This is where left- and right-evaluation came in. On the
left of an assignment A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place. CPL used the terms and
C got them via BCPL's documentation. Viewed like this, BLISS just makes "evaluation" a universal concept.
A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place.
Michael S <already5chosen@yahoo.com> writes:
On Sun, 25 Aug 2024 18:36:46 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 24.08.2024 20:27, Bart wrote:
On 24/08/2024 19:11, Bonita Montero wrote:
I guess C++ is used much more often because you're multiple times
more produdtive than with C. And programming in C++ is a magnitude
less error-prone.
C++ incorporates most of C. So someone can write 'C++' code but can
still have most of the same problems as C.
It's true that C++ decided to inherit unsafe C designs as C being
sort of its base. But a sophisticated programmer would knowingly
avoid the unsafe parts and use the existing safer C++ constructs.
Only that a language allows that you *can* write bad code doesn't
mean you cannot avoid the problems. Of course it would have been
(IMO) better if the unsafe parts were replaced or left out, but
there were portability consideration in C++'s design.
[...]
Safe HLLs without mandatory automatic memory management
I'm not sure what you mean by this description. Do you mean
languages that are otherwise unsafe but have a safe subset?
If not that then please elaborate. What are some examples of
"safe HLLs without mandatory automatic memory management"?
tend to fall
into two categories:
1. Those that already failed to become popular
2. Those for which it will happen soon
It's been amusing reading a discussion of which languages are or are
not high level, without anyone offering a definition of what the
term means. Wikipedia says, roughly, that a high-level language is
one that doesn't provide machine-level access (and IMO that is a
reasonable characterization). Of course no distinction along these
lines is black and white - almost all languages have a loophole or
two - but I expect there is general agreement about which languages
clearly fail that test. In particular, any language that offers
easy access to raw memory addresses (and both C and C++ certainly
do), is not a high-level language in the Wikipedia sense.
Bart <bc@freeuk.com> writes:
[...]
By "access A's value" I mean either read or write access.
Write access does not access the value of an object. [...]
If you're just looking for a Top 10 list, and don't care "Top 10 what?",
then the Tiobe index<https://www.tiobe.com/tiobe-index/> might satisfy. There's lots of controversy about their methodology, despite which I
think it is the least controversial attempt to rank all computer languages.
As I recall, the terms "lvalue" and "rvalue" originated with CPL.
BLISS is a rather strange language. For something supposedly low level
than C, it doesn't have 'goto'.
There is also a key feature that sets it apart from most HLLs: [variable references are always L-values]
On 25/08/2024 19:12, Bonita Montero wrote:
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
OOP, functional programming, generic programming, exceptions.
That isn't surprising. The code you constantly post always uses the most advanced features, uses every toy that is available, and the most
elaborate algorithms.
[...]
On 25.08.2024 20:24, Bart wrote:
On 25/08/2024 19:12, Bonita Montero wrote:
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
This could have been looked up online (e.g. in a Wikipedia article).
OOP, functional programming, generic programming, exceptions.
(And there are yet more.)
That isn't surprising. The code you constantly post always uses the most
advanced features, uses every toy that is available, and the most
elaborate algorithms.
I'm not sure in what bubble you lived the past decades. The listed abstraction examples date back to the 1960's. They were realized in
many programming languages,
many contemporary ones. I suggest to try to understand the concepts
if you want to reach the next experience level. :-)
On Sun, 25 Aug 2024 17:48:14 -0700[...]
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Sun, 25 Aug 2024 18:36:46 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
It's true that C++ decided to inherit unsafe C designs as C being
sort of its base. But a sophisticated programmer would knowingly
avoid the unsafe parts and use the existing safer C++ constructs.
Only that a language allows that you *can* write bad code doesn't
mean you cannot avoid the problems. Of course it would have been
(IMO) better if the unsafe parts were replaced or left out, but
there were portability consideration in C++'s design.
[...]
Safe HLLs without mandatory automatic memory management
I'm not sure what you mean by this description. Do you mean
languages that are otherwise unsafe but have a safe subset?
If not that then please elaborate.
That is nearly always a case in practice, but it does not have to
be. I can't give a counterexample, but I can imagine language
similar to Pascal that has no records with variants and no
procedure Dispose and also hardens few other corners that I
currently forgot about.
What are some examples of
"safe HLLs without mandatory automatic memory management"?
The most prominent examples are Ada and Rust.
tend to fall
into two categories:
1. Those that already failed to become popular
2. Those for which it will happen soon
It's been amusing reading a discussion of which languages are or are
not high level, without anyone offering a definition of what the
term means. Wikipedia says, roughly, that a high-level language is
one that doesn't provide machine-level access (and IMO that is a
reasonable characterization).
I don't like this definition. IMHO, what language does have is at
least as important as what it does not have for the purpose of
estimating its level.
Of course no distinction along these
lines is black and white - almost all languages have a loophole or
two - but I expect there is general agreement about which languages
clearly fail that test. In particular, any language that offers
easy access to raw memory addresses (and both C and C++ certainly
do), is not a high-level language in the Wikipedia sense.
Second amusement: using the term popular without giving any
kind of a metric that measures popularity.
Precise definitions of everything are hard. [...]
Third amusement: any language that has not yet become popular
has already failed to become popular.
There is also "heir apparent' type - languages that are recognized
as not particularly popular now, but believed by many, including
press, to become popular in the future.
That despite at least one language in the 1st category being
pretty well designed, if more than a little over-engineered.
Please, don't keep us in suspense. To what language do you refer?
I thought, that every reader understood that I meant Ada.
Not that I am particularly fond of abstractions when I do see
them.
On 26/08/2024 01:48, Tim Rentsch wrote:
It's been amusing reading a discussion of which languages are or
are not high level, without anyone offering a definition of what
the term means. Wikipedia says, roughly, that a high-level
language is one that doesn't provide machine-level access (and IMO
that is a reasonable characterization). Of course no distinction
along these lines is black and white - almost all languages have a
loophole or two - but I expect there is general agreement about
which languages clearly fail that test. In particular, any
language that offers easy access to raw memory addresses (and both
C and C++ certainly do), is not a high-level language in the
Wikipedia sense.
So, which language do you think is higher level, C++ or Python?
Where might Lisp fit in, or OCaml?
Language 'level' is a linear concept, but the various characteristics
of languages are such that there is really a multidimensional gamut.
Language 'level' is a linear concept ...
Wikipedia classifies C as a high-level language that also supports a
degree of low-level programming, which I think is a fair assessment.
Am 27.08.2024 um 02:16 schrieb Tim Rentsch:
A most unexpected comment. IMO choosing the right abstractions to
define may be the most important skill in developing large systems.
Tell this someone who think C++ doen't provide sth. useful over C.
Am 27.08.2024 um 07:17 schrieb Lawrence D'Oliveiro:
On Tue, 27 Aug 2024 07:10:57 +0200, Bonita Montero wrote:
Am 27.08.2024 um 02:16 schrieb Tim Rentsch:
A most unexpected comment. IMO choosing the right abstractions to
define may be the most important skill in developing large systems.
Tell this someone who think C++ doen't provide sth. useful over C.
Linus Torvalds?
That's hopeless to persuade him.
On Mon, 26 Aug 2024 15:46:02 +0200, David Brown wrote:
Wikipedia classifies C as a high-level language that also supports a
degree of low-level programming, which I think is a fair assessment.
The same could be said of Python.
On 27/08/2024 00:33, Janis Papanagnou wrote:
On 25.08.2024 20:24, Bart wrote:
On 25/08/2024 19:12, Bonita Montero wrote:
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
This could have been looked up online (e.g. in a Wikipedia article).
OOP, functional programming, generic programming, exceptions.
(And there are yet more.)
That isn't surprising. The code you constantly post always uses the most >>> advanced features, uses every toy that is available, and the most
elaborate algorithms.
I'm not sure in what bubble you lived the past decades. The listed
abstraction examples date back to the 1960's. They were realized in
many programming languages,
Perhaps not so much in the ones people used. Assembly? Fortran? Cobol?
There have always been academic languages.
including long existing ones as well as
many contemporary ones. I suggest to try to understand the concepts
if you want to reach the next experience level. :-)
I sometimes use (and implement) such features in scripting code which
has the support to use them effortlessly.
I've rarely needed them for systems programming.
My comments were in connection with their clunky and abstruse
implementations in C++, and BM's habit of posting C++ code full of
gratuitous uses of such features.
Michael S <already5chosen@yahoo.com> writes:
On Sun, 25 Aug 2024 17:48:14 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
That despite at least one language in the 1st category being
pretty well designed, if more than a little over-engineered.
Please, don't keep us in suspense. To what language do you refer?
I thought, that every reader understood that I meant Ada.
I expect many readers did. If I had had to guess I would have
guessed Rust.
Michael S <already5chosen@yahoo.com> writes:
[..concerning "abstraction"..]
Not that I am particularly fond of abstractions when I do see
them.
A most unexpected comment. IMO choosing the right abstractions
to define may be the most important skill in developing large
systems.
Am 27.08.2024 um 09:37 schrieb David Brown:
But it is also fair to say that abstractions are less than you might
see on "big" systems. For systems programming, there is more concern
about the efficiency of the results, ...
C++ is efficient and abstract in one.
We very rarely see exceptions in this field, but OOP is certainly
common now.
You have to accept exceptions with C++ since there are a lot of places
where C++ throws a bad_alloc or system_error.
Classes with non-virtual inheritance are basically cost-free, and
provide
structure, safety, encapsulation and flexibility. Virtual functions have >> overhead, ...
The virtual function overhead isn't notwworthy and not much more over
manual dispatch.
But you certainly can use a range of abstractions in C programming too.
C doesn't supply features to have abstractions like in C++.
I don't think BM's posts are generally good or clear examples of uses
of C++. And I don't think continuously posting "C++ would be ten times
easier" in c.l.c is helpful to anyone.
C is just too much work.
Am 27.08.2024 um 02:16 schrieb Tim Rentsch:
A most unexpected comment. IMO choosing the right abstractions
to define may be the most important skill in developing large
systems.
Tell this someone who think C++ doen't provide sth. useful over C.
Am 27.08.2024 um 07:17 schrieb Lawrence D'Oliveiro:
On Tue, 27 Aug 2024 07:10:57 +0200, Bonita Montero wrote:
Am 27.08.2024 um 02:16 schrieb Tim Rentsch:
A most unexpected comment. IMO choosing the right abstractions to
define may be the most important skill in developing large
systems.
Tell this someone who think C++ doen't provide sth. useful over C.
Linus Torvalds?
That's hopeless to persuade him.
Am 27.08.2024 um 11:32 schrieb David Brown:
On 27/08/2024 10:36, Bonita Montero wrote:
C++ is efficient and abstract in one.
Any simple one-line claim here is clearly going to be wrong.
90% of the C++-abstractions are zero-cost abstractons.
C++ code can be efficient, or abstract, or both, or neither.
Of course it can. Imagine functional programming with a std::sort.
It's abstract since you won't have to deal with the details of the
sort and supply only a comparison function object, but it's still
optimally performant.
You have to accept exceptions with C++ since there are a lot of places
where C++ throws a bad_alloc or system_error.
Incorrect. Like most low-level or systems programmers using C++, I
have exceptions disabled and never use them.
You won't be able to change the runtime's behaviour with that. The
runtime trows bad_alloc or system_error everywhere and if you disable exceptions the application simply terminates if this is thrown.
Incorrect.
I just measured the tim of a ...
virtual int fn( int, int )
... which adds only two ints. The overhead is about one nanosecond
on my Zen4-CPU. And usually you do complex tasks inside the virtual
function so that the call itself doens't participate much in the
overall computation time.
Virtual function overhead will sometimes be worth the cost, and in
some circumstances it can be less than more manual dispatch methods.
But it is not cost-free, and the overhead can most certainly be
relevant if it is used inappropriately.
If the destination of the dispatch varies the overhead is nearly the
same as with static dispatch since most of the time takes the mispre-
dicted branch.
On Sun, 25 Aug 2024 17:48:14 -0700...
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
It's been amusing reading a discussion of which languages are or are
not high level, without anyone offering a definition of what the
term means. Wikipedia says, roughly, that a high-level language is
one that doesn't provide machine-level access (and IMO that is a
reasonable characterization).
I don't like this definition. IMHO, what language does have is at least
as important as what it does not have for the purpose of estimating its level.
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly low level than >>> C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if >>> you declare a variable A, then you can access A's value just by writing A; >>> its address is automatically dereferenced.
Not always. This is where left- and right-evaluation came in. On the
left of an assignment A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place. CPL used the terms and
C got them via BCPL's documentation. Viewed like this, BLISS just makes
"evaluation" a universal concept.
As I recall, the terms "lvalue" and "rvalue" originated with CPL. The
'l' and 'r' suggest the left and right sides of an assignment.
Disclaimer: I have a couple of CPL documents, and I don't see the terms "lvalue" and "rvalue" in a quick look. The PDFs are not searchable. If someone has better information, please post it. Wikipedia does say that
the notion of "l-values" and "r-values" was introduced by CPL.
Am 27.08.2024 um 14:51 schrieb David Brown:
90% of statistics are plucked from the air, including that one.
With C++ this fits. Most abstractions don't have an additional overhead
over a manual implementation.
As I said, you have no idea what you are talking about in the context
of low-level programming.
I told you why it isn't practicable to suppress exceptions in C++
since the runtime uses a lot of exceptions.
Again, you demonstrate your total ignorance of the topic.
Most of the time a nanosecond more doesn't count, especiailly because
usually you do more complex things in a virtual function.
The vast majority of processors produced and sold do not have any kind
of branch prediction.
Not today.
David Brown <david.brown@hesbynett.no> writes:
On 27/08/2024 06:36, Lawrence D'Oliveiro wrote:
On Mon, 26 Aug 2024 15:46:02 +0200, David Brown wrote:
Wikipedia classifies C as a high-level language that also supports aThe same could be said of Python.
degree of low-level programming, which I think is a fair assessment.
Python does not support any significant degree of low-level programming.
A key example of low-level programming is control of hardware, which
on most systems means accessing memory-mapped registers at specific
addresses, reading and writing in specific orders. Python has no
means to do any of that - C and C++ both provide this ability.
(Micropython, a subset of Python targeting microcontrollers and small
systems, has library modules that can do this.)
I've used Python's mmap module to access /dev/kmem on an embedded
Linux system, accessing fixed addresses defined by an FPGA image.
(The mmap module happens to be part of the core Python distribution.)
This is one of several reasons why we have different newsgroups for
different langauges.
Am 27.08.2024 um 14:51 schrieb David Brown:
90% of statistics are plucked from the air, including that one.
With C++ this fits. Most abstractions don't have an additional overhead
over a manual implementation.
As I said, you have no idea what you are talking about in the context
of low-level programming.
I told you why it isn't practicable to suppress exceptions in C++
since the runtime uses a lot of exceptions.
Again, you demonstrate your total ignorance of the topic.
Most of the time a nanosecond more doesn't count, especiailly because
usually you do more complex things in a virtual function.
The vast majority of processors produced and sold do not have any kind
of branch prediction.
Not today.
On 27/08/2024 14:14, Bonita Montero wrote:
There are quite a lot of places in low level programming where you have
I told you why it isn't practicable to suppress exceptions in C++
since the runtime uses a lot of exceptions.
to manage without them. Sometimes you have to do without the runtime as
well. That doesn't mean you can't use C++ itself.
C++ is a lanugage which addresses the lowest level as well as medium abstactions. I like to combine both.
Am 27.08.2024 um 08:47 schrieb Lawrence D'Oliveiro:
I thought you were going to question the competence of someone who had
that attitude ... clearly not.
That's not the question of competence but attitude. Torvalds has a minimalistic mindset and cant't handle abstractions in code.
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly low level than
C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if >>>>> you declare a variable A, then you can access A's value just by writing A;
its address is automatically dereferenced.
Not always. This is where left- and right-evaluation came in. On the >>>> left of an assignment A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place. CPL used the terms and >>>> C got them via BCPL's documentation. Viewed like this, BLISS just makes >>>> "evaluation" a universal concept.
As I recall, the terms "lvalue" and "rvalue" originated with CPL. The
'l' and 'r' suggest the left and right sides of an assignment.
Disclaimer: I have a couple of CPL documents, and I don't see the terms
"lvalue" and "rvalue" in a quick look. The PDFs are not searchable. If >>> someone has better information, please post it. Wikipedia does say that >>> the notion of "l-values" and "r-values" was introduced by CPL.
I presume, since I mentioned the concepts coming from CPL, you are
referring to specifically the short-form terms l- and r-values?
I can't help with those specific terms as the document I have uses a
mixture of terms like "the LH value of...", "left-hand expressions" and
"evaluated in LH mode".
The documents I have are unsearchable PDFs; they appear to be scans of
paper documents.
https://comjnl.oxfordjournals.org/content/6/2/134.full.pdf https://www.ancientgeek.org.uk/CPL/CPL_Elementary_Programming_Manual.pdf
Do you have friendlier documents?
Most typically, first read about abstract concept goes
straight above my head.
Python does not support any significant degree of low-level programming.
A key example of low-level programming is control of hardware, which on
most systems means accessing memory-mapped registers at specific
addresses, reading and writing in specific orders. Python has no means
to do any of that - C and C++ both provide this ability.
On 26/08/2024 13:30, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly low level than >>> C, it doesn't have 'goto'.Not always. This is where left- and right-evaluation came in. On the
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if >>> you declare a variable A, then you can access A's value just by writing A; >>> its address is automatically dereferenced.
left of an assignment A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place. CPL used the terms and
C got them via BCPL's documentation. Viewed like this, BLISS just makes
"evaluation" a universal concept.
That doesn't explain why one language requires an explcition dereference in the source code, and the other doesn't.
By "access A's value" I mean either read or write access.
A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place.
This /is/ confusing as it suggests a different rank for A depending on whether it is an lvalue or rvalue, eg. some difference in level of indirection. In fact that is the same on both sides.
My point was that HLLs typically read or write values of variables without extra syntax.
Given a declaration like 'int A' then:
BLISS C
Read or write A's value .A A
Get A's address A &A
... or with ctypes and an external shared library - not
directly from Python.
On Tue, 27 Aug 2024 09:44:40 +0200, David Brown wrote:
Python does not support any significant degree of low-level programming.
A key example of low-level programming is control of hardware, which on
most systems means accessing memory-mapped registers at specific
addresses, reading and writing in specific orders. Python has no means
to do any of that - C and C++ both provide this ability.
I’ve got news for you: this kind of thing is perfectly doable in Python <https://docs.python.org/3/library/ctypes.html>.
Bart <bc@freeuk.com> writes:
On 26/08/2024 13:30, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly low level thanNot always. This is where left- and right-evaluation came in. On the
C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: usually if >>>> you declare a variable A, then you can access A's value just by writing A; >>>> its address is automatically dereferenced.
left of an assignment A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place. CPL used the terms and >>> C got them via BCPL's documentation. Viewed like this, BLISS just makes >>> "evaluation" a universal concept.
That doesn't explain why one language requires an explcition dereference in >> the source code, and the other doesn't.
It does for me. If you think I can help, maybe you could ask some more questions as I don't know what else to say. BLISS uses addresses
explicitly, so the rvalue/lvalue distincion is not a perfect match for
what's going on, but it's close enough that I find it helpful.
By "access A's value" I mean either read or write access.
A denotes a "place" to receive a value. On the
right, it denotes a value obtained from a place.
This /is/ confusing as it suggests a different rank for A depending on
whether it is an lvalue or rvalue, eg. some difference in level of
indirection. In fact that is the same on both sides.
I don't know what you mean by rank here. The whole point of two
different evaluations -- as an rvalue or an lvalue -- can be seen
(rather too crudely I fear) as adding one more level of indirection so
that what we expect to happen (when we've got used to modern programming languages), happens.
My point was that HLLs typically read or write values of variables without >> extra syntax.
Indeed, and BLISS is not like that. I had hoped to shed some light on
why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:
BLISS C
Read or write A's value .A A
I don't think that's right. To change the value at address A (what I
think you mean by "write A's value") you write
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly
low level than C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs:
usually if you declare a variable A, then you can access A's
value just by writing A; its address is automatically
dereferenced.
Not always. This is where left- and right-evaluation came in.
On the left of an assignment A denotes a "place" to receive a
value. On the right, it denotes a value obtained from a place.
CPL used the terms and C got them via BCPL's documentation.
Viewed like this, BLISS just makes "evaluation" a universal
concept.
As I recall, the terms "lvalue" and "rvalue" originated with CPL.
The 'l' and 'r' suggest the left and right sides of an
assignment.
Disclaimer: I have a couple of CPL documents, and I don't see
the terms "lvalue" and "rvalue" in a quick look. The PDFs are
not searchable. If someone has better information, please post
it. Wikipedia does say that the notion of "l-values" and
"r-values" was introduced by CPL.
I presume, since I mentioned the concepts coming from CPL, you are
referring to specifically the short-form terms l- and r-values?
I can't help with those specific terms as the document I have uses
a mixture of terms like "the LH value of...", "left-hand
expressions" and "evaluated in LH mode".
The documents I have are unsearchable PDFs; they appear to be
scans of paper documents.
https://comjnl.oxfordjournals.org/content/6/2/134.full.pdf
https://www.ancientgeek.org.uk/CPL/CPL_Elementary_Programming_Manual.pdf
Do you have friendlier documents?
The earliest that is searchable has this title page:
UNIVERSITY OF LONDON INSTITUTE OF COMPUTER SCIENCE
*************************************************
THE UNIVERSITY MATHEMATICAL LABORATORY, CAMBRIDGE
*************************************************
CPL ELEMENTARY PROGRAMMING MANUAL
Edition I (London)
This document, written by the late John Buxton, was preserved by
Bill Williams, formerly of London University?s Atlas support team.
Bill has generously made it available to Dik Leatherdale who has
OCRed and otherwise transcribed it for the Web. All errors should
be reported to dik@leatherdale.net. The original appearance is
respected as far as possible, but program text and narrative are
distinguished by the use of different fonts. Transcriber's
additions and 'corrections' are in red, hyperlinks in underlined
purple. A contents list and a selection of references have been
added inside the back cover.
March 1965
I don't know where I got it from. The other searchable one is just
a PDF is the oft-cited paper "The main features of CPL" by Barron
et. al.
On Mon, 26 Aug 2024 17:55:21 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Sun, 25 Aug 2024 17:48:14 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
That despite at least one language in the 1st category being
pretty well designed, if more than a little over-engineered.
Please, don't keep us in suspense. To what language do you refer?
I thought, that every reader understood that I meant Ada.
I expect many readers did. If I had had to guess I would have
guessed Rust.
It means that derogatory overtones of my past comments about Rust were
too subtle.
Am 28.08.2024 um 01:44 schrieb Lawrence D'Oliveiro:
On the contrary, the Linux kernel is full of abstractions: the device
layer, the network layer, the filesystem layer, the security layer ...
C's abstraction are very low level.
On 28/08/2024 00:53, Lawrence D'Oliveiro wrote:
On Tue, 27 Aug 2024 09:44:40 +0200, David Brown wrote:
Python does not support any significant degree of low-level
programming.
A key example of low-level programming is control of hardware, which
on most systems means accessing memory-mapped registers at specific
addresses, reading and writing in specific orders. Python has no
means to do any of that - C and C++ both provide this ability.
I’ve got news for you: this kind of thing is perfectly doable in Python
<https://docs.python.org/3/library/ctypes.html>.
It's Python calling a special module to do the dirty work.
That's not far removed from Python just invoking an external C program
to do the job.
By contrast, my scripting language can directly do the low level stuff.
Am 27.08.2024 um 20:54 schrieb David Brown:
And you were completely wrong when you said that. Perhaps in /your/
field of programming you are correct - but you are ignoring the rest
of the world.
You've to omit nearly the whole standard libary to avoid exceptions.
F.e. everything that has an allocator may throw bad_alloc.
Often that is correct. Often it is /not/ correct.
It's correct nearly every time since usually you've more than a dozen instructions in a virtual function.
For every one of your favourite big x86 chips sold, there will be a
hundred small microcontrollers - none of which has branch prediction.
Even the simple Cortex CPUs have a branch prediciton.
But do you think the one in my watch has one?
(I am not even sure why you thought branch prediction was relevant
here.)
No, they don't. The Cortex-M cores do not have branch prediction.
Am 28.08.2024 um 11:26 schrieb David Brown:
No, they don't. ..
Then the branch is the most expensive part, no matter if
you're doing a call or a table-dispatch within a function.
On 25/08/2024 17:20, tTh wrote:
On 8/25/24 17:30, Bart wrote:
So what language goes between Assembly and C?
Forth ?
I had in mind languages classed as 'HLLs'. I'm not sure if Forth
counts.
Am 28.08.2024 um 12:43 schrieb Michael S:
Virtual function call is typically implemented as double
indirection, so it ends up even slower than C-style call through
function pointer.
If you have a function<>-object there's no double indirection.
On Wed, 28 Aug 2024 11:26:03 +0200
David Brown <david.brown@hesbynett.no> wrote:
(I am not even sure why you thought branch prediction was relevant
here.)
It is relevant.
Sophisticated branch prediction + BTBs + deep speculation working
together is a main reason for very good common-case performance of
virtual function calls on "big" CPUs.
On Mon, 26 Aug 2024 17:16:06 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
[..concerning "abstraction"..]
Not that I am particularly fond of abstractions when I do see
them.
A most unexpected comment. IMO choosing the right abstractions
to define may be the most important skill in developing large
systems.
You are probably right, but me being me, I am rarely able to grasp
pure abstract things. Most typically, first read about abstract
concept goes straight above my head.
It can't be helped by few examples, but success is not
guaranteed.
Even after I seemingly grasped the principle, when I start using
an instance of abstract thing, it's hard for me to stop thinking
about gears and toothed wheels rotating under the hood.
On Sun, 25 Aug 2024 18:26:57 +0100
Bart <bc@freeuk.com> wrote:
On 25/08/2024 17:20, tTh wrote:
On 8/25/24 17:30, Bart wrote:
So what language goes between Assembly and C?
Forth ?
I had in mind languages classed as 'HLLs'. I'm not sure if Forth
counts.
They say that Forth is a HLL
https://www.forth.com/forth/
I tend to agree with them.
My personal bar for HLL is pretty low - any language in which one can
write useful program (not necessarily big useful program or *very*
useful program) in a way that does not depend on instruction set of underlying hardware is HLL.
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly
low level than C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs:
usually if you declare a variable A, then you can access A's
value just by writing A; its address is automatically
dereferenced.
Not always. This is where left- and right-evaluation came in.
On the left of an assignment A denotes a "place" to receive a
value. On the right, it denotes a value obtained from a place.
CPL used the terms and C got them via BCPL's documentation.
Viewed like this, BLISS just makes "evaluation" a universal
concept.
As I recall, the terms "lvalue" and "rvalue" originated with CPL.
The 'l' and 'r' suggest the left and right sides of an
assignment.
Disclaimer: I have a couple of CPL documents, and I don't see
the terms "lvalue" and "rvalue" in a quick look. The PDFs are
not searchable. If someone has better information, please post
it. Wikipedia does say that the notion of "l-values" and
"r-values" was introduced by CPL.
I presume, since I mentioned the concepts coming from CPL, you are
referring to specifically the short-form terms l- and r-values?
I can't help with those specific terms as the document I have uses
a mixture of terms like "the LH value of...", "left-hand
expressions" and "evaluated in LH mode".
The documents I have are unsearchable PDFs; they appear to be
scans of paper documents.
https://comjnl.oxfordjournals.org/content/6/2/134.full.pdf
https://www.ancientgeek.org.uk/CPL/CPL_Elementary_Programming_Manual.pdf >>>
Do you have friendlier documents?
The earliest that is searchable has this title page:
UNIVERSITY OF LONDON INSTITUTE OF COMPUTER SCIENCE
*************************************************
THE UNIVERSITY MATHEMATICAL LABORATORY, CAMBRIDGE
*************************************************
CPL ELEMENTARY PROGRAMMING MANUAL
Edition I (London)
This document, written by the late John Buxton, was preserved by
Bill Williams, formerly of London University?s Atlas support team.
Bill has generously made it available to Dik Leatherdale who has
OCRed and otherwise transcribed it for the Web. All errors should
be reported to dik@leatherdale.net. The original appearance is
respected as far as possible, but program text and narrative are
distinguished by the use of different fonts. Transcriber's
additions and 'corrections' are in red, hyperlinks in underlined
purple. A contents list and a selection of references have been
added inside the back cover.
March 1965
I don't know where I got it from. The other searchable one is just
a PDF is the oft-cited paper "The main features of CPL" by Barron
et. al.
My understanding is the terms l-value and r-value, along with
several other terms widely used in relation to programming
languages, became widely used following a summer(?) course taught
by Christopher Strachey. Some of the other terms are referential transparency and parametric polymorphism, IIRC.
https://en.wikipedia.org/wiki/Fundamental_Concepts_in_Programming_Languages
I believe it is possible to track down the notes from that course,
if a diligent web search is employed. I remember reading a copy
some years ago after finding one on the internet.
On 28/08/2024 00:49, Ben Bacarisse wrote:
Indeed, and BLISS is not like that. I had hoped to shed some light on
why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:I don't think that's right. To change the value at address A (what I
BLISS C
Read or write A's value .A A
think you mean by "write A's value") you write
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
OK. That's just makes it more bizarre than I'd thought.
The example I saw
included these lines:
GETNUM(X); ! returns a value via X
Y = STEP(.X);
PUTNUM(.Y)
So in an rvalue context: X reads its address; while .X reads its
value.
But in an lvalue one: Y writes its value; .Y may not be defined
It looks asymmetric. C like most languages is symmetric, you write 'A = A' with the same syntax on both sides.
I assume that in BLISS, A = A is legal, but does something odd like copy
A's address into itself.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
BLISS is a rather strange language. For something supposedly
low level than C, it doesn't have 'goto'.
It is also typeless.
There is also a key feature that sets it apart from most HLLs: >>>>>>>> usually if you declare a variable A, then you can access A's
value just by writing A; its address is automatically
dereferenced.
Not always. This is where left- and right-evaluation came in.
On the left of an assignment A denotes a "place" to receive a
value. On the right, it denotes a value obtained from a place.
CPL used the terms and C got them via BCPL's documentation.
Viewed like this, BLISS just makes "evaluation" a universal
concept.
As I recall, the terms "lvalue" and "rvalue" originated with CPL.
The 'l' and 'r' suggest the left and right sides of an
assignment.
Disclaimer: I have a couple of CPL documents, and I don't see
the terms "lvalue" and "rvalue" in a quick look. The PDFs are
not searchable. If someone has better information, please post
it. Wikipedia does say that the notion of "l-values" and
"r-values" was introduced by CPL.
I presume, since I mentioned the concepts coming from CPL, you are
referring to specifically the short-form terms l- and r-values?
I can't help with those specific terms as the document I have uses
a mixture of terms like "the LH value of...", "left-hand
expressions" and "evaluated in LH mode".
The documents I have are unsearchable PDFs; they appear to be
scans of paper documents.
https://comjnl.oxfordjournals.org/content/6/2/134.full.pdf
https://www.ancientgeek.org.uk/CPL/CPL_Elementary_Programming_Manual.pdf >>>>
Do you have friendlier documents?
The earliest that is searchable has this title page:
UNIVERSITY OF LONDON INSTITUTE OF COMPUTER SCIENCE
*************************************************
THE UNIVERSITY MATHEMATICAL LABORATORY, CAMBRIDGE
*************************************************
CPL ELEMENTARY PROGRAMMING MANUAL
Edition I (London)
This document, written by the late John Buxton, was preserved by
Bill Williams, formerly of London University?s Atlas support team.
Bill has generously made it available to Dik Leatherdale who has
OCRed and otherwise transcribed it for the Web. All errors should
be reported to dik@leatherdale.net. The original appearance is
respected as far as possible, but program text and narrative are
distinguished by the use of different fonts. Transcriber's
additions and 'corrections' are in red, hyperlinks in underlined
purple. A contents list and a selection of references have been
added inside the back cover.
March 1965
I don't know where I got it from. The other searchable one is just
a PDF is the oft-cited paper "The main features of CPL" by Barron
et. al.
My understanding is the terms l-value and r-value, along with
several other terms widely used in relation to programming
languages, became widely used following a summer(?) course taught
by Christopher Strachey. Some of the other terms are referential
transparency and parametric polymorphism, IIRC.
The earlier ('65 and '66) writings about CPL that I've seen all use the longer terms, and those lectures certainly use the short forms, so it
seems clear this is when they came about and, since he is the sole
author of the notes (unlike all the CPL documents that are groups
efforts), it's also likely he invented the terms.
https://en.wikipedia.org/wiki/Fundamental_Concepts_in_Programming_Languages >>
I believe it is possible to track down the notes from that course,
if a diligent web search is employed. I remember reading a copy
some years ago after finding one on the internet.
I suspect there is no copyright-free PDF as the notes were published by
an academic press.
None the less well worth a read, even nearly 60 years later. Is it a
shame that that is the case?
Am 28.08.2024 um 15:52 schrieb Thiago Adams:
You have to deallocate only if the ownership still with the same object.
This is not the case when the object is moved.
If the compiler sees that it is moved the whole deallocation
is usually opimized away for the non-exception code path.
To create view object you need a new object because destructor cannot
be disabled.
It's optimized away for the non-exception code-path if the compiler
sees it.
Const-objects are there not to be modified. If you've got logical
constness you can cast away the const with const_cast and move its
contents. But casting away const is unclean mostly in C and C++.
excuses..
In C yoU'd have to do the same. And there's only one type of cast
and you could mistakenly cast to a non-fitting type whereas in C++
you've got const_cast<> which may only differ in const-ness.
It was create latter after people realize std::string was bad.
The same for std::array etc... almost everything is a fix in C++.
Array is there to have iterator-debugging on sth. that looks like
a C-array. With C-arrays there's no such feature and it's harder
to find according bugs. If I use static const C-style arrays which
are directed to the DATA-ssegment I always use a span<> on it to
have iterator-debugging, whereas in C you have to take a lot of
care.
(I don't blame people creating C++ in the past, but I think now we
have information sufficient to do better choices and this choices are
not begin made).
C is five to ten times more code for the same task.
On 27/08/2024 00:33, Janis Papanagnou wrote:
On 25.08.2024 20:24, Bart wrote:
On 25/08/2024 19:12, Bonita Montero wrote:
Am 25.08.2024 um 18:28 schrieb Michael S:
Define "abstraction".
This could have been looked up online (e.g. in a Wikipedia article).
OOP, functional programming, generic programming, exceptions.
(And there are yet more.)
That isn't surprising. The code you constantly post always uses the most >>> advanced features, uses every toy that is available, and the most
elaborate algorithms.
I'm not sure in what bubble you lived the past decades. The listed
abstraction examples date back to the 1960's. They were realized in
many programming languages,
Perhaps not so much in the ones people used. Assembly? Fortran? Cobol?
There have always been academic languages.
including long existing ones as well as
many contemporary ones. I suggest to try to understand the concepts
if you want to reach the next experience level. :-)
I sometimes use (and implement) such features in scripting code which
has the support to use them effortlessly.
I've rarely needed them for systems programming.
My comments were in connection with their clunky and abstruse
implementations in C++, and BM's habit of posting C++ code full of
gratuitous uses of such features.
It was create latter after people realize std::string was bad.
The same for std::array etc... almost everything is a fix in C++.
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
This reminds me someone (I think it was a university professor
in the 1980's) saying that BASIC became a low-level language when
some vendors introduced 'peek' and 'poke' into the language's set
of functions.
Bart <bc@freeuk.com> writes:
On 28/08/2024 00:49, Ben Bacarisse wrote:
Indeed, and BLISS is not like that. I had hoped to shed some light on
why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:I don't think that's right. To change the value at address A (what I
BLISS C
Read or write A's value .A A
think you mean by "write A's value") you write
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
OK. That's just makes it more bizarre than I'd thought.
Curious. It's what makes it consistent, though it is definitely an
uncommon approach.
The example I saw
included these lines:
GETNUM(X); ! returns a value via X
Y = STEP(.X);
PUTNUM(.Y)
So in an rvalue context: X reads its address; while .X reads its
value.
The whole point is to remove the two contexts. A variable name is
/always/ an lvalue (which is why it can be assigned). C has an implicit lvalue to rvalue conversion in the contexts you have come to expect it.
BLISS does not. You always need a dot to convert to an rvalue.
But in an lvalue one: Y writes its value; .Y may not be defined
It looks asymmetric. C like most languages is symmetric, you write 'A = A' >> with the same syntax on both sides.
Since assignment is inherently asymmetric (you can't write 3 = A but you
can write A = 3) C's syntactic symmetry hides a semantic difference.
What is needed on the two sides is not the same.
I assume that in BLISS, A = A is legal, but does something odd like copy
A's address into itself.
What's odd about that? And why call is a copy operation? Do you think
of A = 42 as a copy operation? BLISS is a low-level system language.
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
Yes. And deliberately so.
On 28.08.2024 20:37, Scott Lurndal wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
This reminds me someone (I think it was a university professor
in the 1980's) saying that BASIC became a low-level language when
some vendors introduced 'peek' and 'poke' into the language's set
of functions.
There were BASIC interpreters (and compilers) in the 1970s
that supported calling functions written in other languages.
The HP-3000 BASIC interpreter and compiler, for example.
Myself I've never seen or worked with such a system.
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
This reminds me someone (I think it was a university professor
in the 1980's) saying that BASIC became a low-level language when
some vendors introduced 'peek' and 'poke' into the language's set
of functions.
There were BASIC interpreters (and compilers) in the 1970s
that supported calling functions written in other languages.
The HP-3000 BASIC interpreter and compiler, for example.
On 28/08/2024 15:57, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 28/08/2024 00:49, Ben Bacarisse wrote:Curious. It's what makes it consistent, though it is definitely an
Indeed, and BLISS is not like that. I had hoped to shed some light on >>>> why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:I don't think that's right. To change the value at address A (what I
BLISS C
Read or write A's value .A A
think you mean by "write A's value") you write
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
OK. That's just makes it more bizarre than I'd thought.
uncommon approach.
The example I sawThe whole point is to remove the two contexts. A variable name is
included these lines:
GETNUM(X); ! returns a value via X
Y = STEP(.X);
PUTNUM(.Y)
So in an rvalue context: X reads its address; while .X reads its
value.
/always/ an lvalue (which is why it can be assigned). C has an implicit
lvalue to rvalue conversion in the contexts you have come to expect it.
BLISS does not. You always need a dot to convert to an rvalue.
This is the kind of thing I meant a few posts back. You don't need to take
A (which refers to some place where you can store values), and tell it to fetch that value. Most HLLs will do that without being told.
(My point was that that was a distinguishing feature of HLLs, which is missing in Forth for example.)
But in an lvalue one: Y writes its value; .Y may not be definedSince assignment is inherently asymmetric (you can't write 3 = A but you
It looks asymmetric. C like most languages is symmetric, you write 'A = A' >>> with the same syntax on both sides.
can write A = 3) C's syntactic symmetry hides a semantic difference.
What is needed on the two sides is not the same.
I would argue that it is exactly the same.
I assume that in BLISS, A = A is legal, but does something odd like copy >>> A's address into itself.What's odd about that? And why call is a copy operation? Do you think
of A = 42 as a copy operation? BLISS is a low-level system language.
Why do you mean by call?
On 28.08.2024 22:42, Tim Rentsch wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 27.08.2024 03:16, Tim Rentsch wrote:
[...]
For example, in the Wikipedia entry sense of the term, the original
BASIC is a high-level language, but I think most people would agree
that it is not a very powerful language.
I note you are saying "_original_ BASIC".
Yes. And deliberately so.
That was obvious to me [...]
On 28/08/2024 12:21, Michael S wrote:
On Sun, 25 Aug 2024 18:26:57 +0100
Bart <bc@freeuk.com> wrote:
On 25/08/2024 17:20, tTh wrote:
On 8/25/24 17:30, Bart wrote:
So what language goes between Assembly and C?
Forth ?
I had in mind languages classed as 'HLLs'. I'm not sure if Forth
counts.
They say that Forth is a HLL
https://www.forth.com/forth/
I tend to agree with them.
From 'forth.com', and a commercial site at that? They're going to be completely impartial of course!
This is the Ackermann function in Forth, from https://rosettacode.org/wiki/Category:Forth :
: acker ( m n -- u )
over 0= IF nip 1+ EXIT THEN
swap 1- swap ( m-1 n -- )
dup 0= IF 1+ recurse EXIT THEN
1- over 1+ swap recurse recurse ;
Well, it's not assembly, but it doesn't have one characteristic of HLLs
which is readability. The code for Ackermann can usually be trivially
derived from its mathemetical definition; not so here.
So I say it fails the HLL test. But if it's not a HLL, it's also fails
on low-level control: the above doesn't concern itself with types or bitwidths for example; you might consider that a HLL trait, but it's one
that belongs the other side of C rather than below.
Bart <bc@freeuk.com> writes:
On 28/08/2024 15:57, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 28/08/2024 00:49, Ben Bacarisse wrote:Curious. It's what makes it consistent, though it is definitely an
Indeed, and BLISS is not like that. I had hoped to shed some light on >>>>> why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:I don't think that's right. To change the value at address A (what I >>>>> think you mean by "write A's value") you write
BLISS C
Read or write A's value .A A
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
OK. That's just makes it more bizarre than I'd thought.
uncommon approach.
The example I sawThe whole point is to remove the two contexts. A variable name is
included these lines:
GETNUM(X); ! returns a value via X
Y = STEP(.X);
PUTNUM(.Y)
So in an rvalue context: X reads its address; while .X reads its
value.
/always/ an lvalue (which is why it can be assigned). C has an implicit >>> lvalue to rvalue conversion in the contexts you have come to expect it.
BLISS does not. You always need a dot to convert to an rvalue.
This is the kind of thing I meant a few posts back. You don't need to take >> A (which refers to some place where you can store values), and tell it to
fetch that value. Most HLLs will do that without being told.
(My point was that that was a distinguishing feature of HLLs, which is
missing in Forth for example.)
We are talking at cross purposes then. I was not addressing anything
about your view of what makes an HLL.
But in an lvalue one: Y writes its value; .Y may not be definedSince assignment is inherently asymmetric (you can't write 3 = A but you >>> can write A = 3) C's syntactic symmetry hides a semantic difference.
It looks asymmetric. C like most languages is symmetric, you write 'A = A' >>>> with the same syntax on both sides.
What is needed on the two sides is not the same.
I would argue that it is exactly the same.
How do you argue that, given that A=3 is allowed and 3=A is not?
...
I assume that in BLISS, A = A is legal, but does something odd like copy >>>> A's address into itself.What's odd about that? And why call is a copy operation? Do you think
of A = 42 as a copy operation? BLISS is a low-level system language.
Why do you mean by call?
Typo. I meant to write... And why call /it/ a copy-operation? Do you
think of A = 42 as a copy operation?
On 29/08/2024 00:43, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 28/08/2024 15:57, Ben Bacarisse wrote:We are talking at cross purposes then. I was not addressing anything
Bart <bc@freeuk.com> writes:
On 28/08/2024 00:49, Ben Bacarisse wrote:Curious. It's what makes it consistent, though it is definitely an
Indeed, and BLISS is not like that. I had hoped to shed some light on >>>>>> why there is some logic to BLISS's rather idiosyncratic design.
Given a declaration like 'int A' then:I don't think that's right. To change the value at address A (what I >>>>>> think you mean by "write A's value") you write
BLISS C
Read or write A's value .A A
A = 42;
in BLISS. And to add one to the value at address A you write
A = .A + 1;
OK. That's just makes it more bizarre than I'd thought.
uncommon approach.
The example I sawThe whole point is to remove the two contexts. A variable name is
included these lines:
GETNUM(X); ! returns a value via X
Y = STEP(.X);
PUTNUM(.Y)
So in an rvalue context: X reads its address; while .X reads its
value.
/always/ an lvalue (which is why it can be assigned). C has an implicit >>>> lvalue to rvalue conversion in the contexts you have come to expect it. >>>> BLISS does not. You always need a dot to convert to an rvalue.
This is the kind of thing I meant a few posts back. You don't need to take >>> A (which refers to some place where you can store values), and tell it to >>> fetch that value. Most HLLs will do that without being told.
(My point was that that was a distinguishing feature of HLLs, which is
missing in Forth for example.)
about your view of what makes an HLL.
How do you argue that, given that A=3 is allowed and 3=A is not?But in an lvalue one: Y writes its value; .Y may not be definedSince assignment is inherently asymmetric (you can't write 3 = A but you >>>> can write A = 3) C's syntactic symmetry hides a semantic difference.
It looks asymmetric. C like most languages is symmetric, you write 'A = A'
with the same syntax on both sides.
What is needed on the two sides is not the same.
I would argue that it is exactly the same.
I explained that. LHS and RHS can be identical terms for assignment in
pretty much every aspect, but there are extra constraints on the LHS.
In the case of :
(c?a:b) = (z?x:y);
C won't allow it, but some other languages will.
Remember that the programmer can only express their intentions in the form
of syntax.
...
Typo. I meant to write... And why call /it/ a copy-operation? Do youI assume that in BLISS, A = A is legal, but does something odd like copy >>>>> A's address into itself.What's odd about that? And why call is a copy operation? Do you think >>>> of A = 42 as a copy operation? BLISS is a low-level system language.
Why do you mean by call?
think of A = 42 as a copy operation?
If '=' means assignment, then what else is it?
I don't know why you're always so contradictory. Is it a game trying to
catch me out on some pendanty? It seems to be popular here.
This subthread started with me asking which HLL goes between Assembly and
C, if C was supposedly mid-level. I don't know how it got on discussing
what exactly assignment means.
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in
pretty much every aspect, but there are extra constraints on the LHS.
So you use "exactly the same" to mean "exactly the same except for the differences".
I don't know why you're always so contradictory. Is it a game trying to
catch me out on some pendanty? It seems to be popular here.
I wanted to explain how BLISS gets rid of the lvalue/rvalue distinction because you seemed to have misunderstood it.
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment inSo you use "exactly the same" to mean "exactly the same except for the
pretty much every aspect, but there are extra constraints on the LHS.
differences".
No, I do mean exactly the same, both in terms of syntax and (in my implementations, which are likely typical) internal representation of those terms.
There are no differences other than where the type system says your code is invalid. So are no differences when considering only valid programs.
This program in my language:
42 := 42
is valid syntax.
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in >>>>> pretty much every aspect, but there are extra constraints on the LHS. >>>> So you use "exactly the same" to mean "exactly the same except for the >>>> differences".
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of those >>> terms.
There are no differences other than where the type system says your code is >>> invalid. So are no differences when considering only valid programs.
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not
an honest thing to do. You are arguing for the sake if it, and in a
dishonest way too.
It's also valid syntax in C, with a constraint violation that can be
"caught later on" in an implementation of C, just like in Bart's
language.
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in >>>> pretty much every aspect, but there are extra constraints on the LHS.So you use "exactly the same" to mean "exactly the same except for the
differences".
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of those >> terms.
There are no differences other than where the type system says your code is >> invalid. So are no differences when considering only valid programs.
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not
an honest thing to do. You are arguing for the sake if it, and in a dishonest way too.
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in >>>>>> pretty much every aspect, but there are extra constraints on the LHS. >>>>> So you use "exactly the same" to mean "exactly the same except for the >>>>> differences".
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of those
terms.
There are no differences other than where the type system says your code is
invalid. So are no differences when considering only valid programs.
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not >>> an honest thing to do. You are arguing for the sake if it, and in a
dishonest way too.
It's also valid syntax in C, with a constraint violation that can be
"caught later on" in an implementation of C, just like in Bart's
language.
Have you taken Bart's bait and are now discussing a narrower context?
The claim that C's assignment is symmetric and what is required on the
two sides is exactly the same is junk. C's assignment has different
syntax on each side, and what is required is even more strict.
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in >>>>>>> pretty much every aspect, but there are extra constraints on the LHS. >>>>>> So you use "exactly the same" to mean "exactly the same except for the >>>>>> differences".
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of those
terms.
There are no differences other than where the type system says your code is
invalid. So are no differences when considering only valid programs. >>>>>
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not >>>> an honest thing to do. You are arguing for the sake if it, and in a
dishonest way too.
It's also valid syntax in C, with a constraint violation that can be
"caught later on" in an implementation of C, just like in Bart's
language.
Have you taken Bart's bait and are now discussing a narrower context?
The claim that C's assignment is symmetric and what is required on the
two sides is exactly the same is junk. C's assignment has different
syntax on each side, and what is required is even more strict.
In the ISO C grammar for assignment, there is a "unary expression" on
the left and an "assignment expression" on the right. That's just a particular factoring of the grammar that implementors don't have to
follow, if the correct results are produced.
Under a parser generator tool we could have a production rule like
expr '=' expr , where the '=' token has an elsewhere-declared
associativity and precedence.
The basic idea that the same syntactic kind of thing is on both sides of
a C assignment (with an additional lvalue constraint) is valid;
it's just not literally true if we are discussing the details of how
ISO C expresses the grammar.
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in >>>>>>>> pretty much every aspect, but there are extra constraints on the LHS. >>>>>>> So you use "exactly the same" to mean "exactly the same except for the >>>>>>> differences".
No, I do mean exactly the same, both in terms of syntax and (in my >>>>>> implementations, which are likely typical) internal representation of those
terms.
There are no differences other than where the type system says your code is
invalid. So are no differences when considering only valid programs. >>>>>>
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not >>>>> an honest thing to do. You are arguing for the sake if it, and in a >>>>> dishonest way too.
It's also valid syntax in C, with a constraint violation that can be
"caught later on" in an implementation of C, just like in Bart's
language.
Have you taken Bart's bait and are now discussing a narrower context?
The claim that C's assignment is symmetric and what is required on the
two sides is exactly the same is junk. C's assignment has different
syntax on each side, and what is required is even more strict.
In the ISO C grammar for assignment, there is a "unary expression" on
the left and an "assignment expression" on the right. That's just a
particular factoring of the grammar that implementors don't have to
follow, if the correct results are produced.
I can't see what it is you object to in what I wrote. I don't disagree
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side).
Under a parser generator tool we could have a production rule like
expr '=' expr , where the '=' token has an elsewhere-declared
associativity and precedence.
The basic idea that the same syntactic kind of thing is on both sides of
a C assignment (with an additional lvalue constraint) is valid;
it's just not literally true if we are discussing the details of how
ISO C expresses the grammar.
A C program that has the wrong syntax (for example x+1) on the left hand
side of an assignment must be rejected. I'm not relying on some fussy definition about how the syntax is written but making a point that what
is required on each side is not the exactly same thing. Do you really disagree with that?
A C program that has the wrong syntax (for example x+1) on the left hand side of an assignment must be rejected.
I can't see what it is you object to in what I wrote. I don't disagree
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side).
In most HLLs you use the same syntax whether for lvalue or rvalue
(eg. A = A).
Bart <bc@freeuk.com> writes:
[...]
So what exactly is different about the LHS and RHS here:
A = A;
The RHS is evaluated to determine the current value stored in the object named A. The LHS is evaluated to determine the object that's designated
by the name A; its current value is irrelevant.
In C terms, the RHS undergoes *lvalue conversion*, where an expression
that's an lvalue is converted to the value stored in the designated
object. The LHS does not undergo lvalue conversion.
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A'
is also valid, there is a hidden mismatch in indirection levels
between left and right. It is asymmetric while in C it is symmetric,
although seem to disagree on that latter point.)
Because BLISS, unlike C, does not have implicit lvalue conversion; the
prefix "." operator performs explicit lvalue conversion. I presume the
"." operator isn't specific to assignments.
In C, the LHS and RHS are evaluated differently. In BLISS, they're
evaluated in the same way, requiring an explicit operator to do what
done implicitly by context in C. I'd call the former asymmetric and the latter symmetric.
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 8/29/24 12:06, Ben Bacarisse wrote:
...
I can't see what it is you object to in what I wrote. I don't disagree
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side).
No - the only requirement is that a diagnostic be produced. A fully
conforming implementation of C is allowed to accept such code and then
generate an executable; if you choose to execute the executable, the
behavior is undefined.
Sorry, I used a term incorrectly. To put it informally, you must be
told that "this is not C". Not everything is C even if a C compiler
will accept FORTRAN code as an extension.
On 8/29/24 12:06, Ben Bacarisse wrote:
...
I can't see what it is you object to in what I wrote. I don't disagree
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side).
No - the only requirement is that a diagnostic be produced. A fully conforming implementation of C is allowed to accept such code and then generate an executable; if you choose to execute the executable, the
behavior is undefined.
Bart <bc@freeuk.com> writes:
On 29/08/2024 21:30, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]
So what exactly is different about the LHS and RHS here:The RHS is evaluated to determine the current value stored in the
A = A;
object
named A. The LHS is evaluated to determine the object that's designated >>> by the name A; its current value is irrelevant.
Sure, but the same thing happens on both sides: one ends up performing
a Read via that Lvalue, and the other does a Write via that Lvalue.
The read is done by converting the lvalue to its value, which is not an lvalue. Please read the discussion of "lvalue conversion" in the C
standard.
In C terms, the RHS undergoes *lvalue conversion*, where an expression
that's an lvalue is converted to the value stored in the designated
object. The LHS does not undergo lvalue conversion.
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A'Because BLISS, unlike C, does not have implicit lvalue conversion;
is also valid, there is a hidden mismatch in indirection levels
between left and right. It is asymmetric while in C it is symmetric,
although seem to disagree on that latter point.)
the
prefix "." operator performs explicit lvalue conversion. I presume the
"." operator isn't specific to assignments.
But it must have that conversion on the LHS, otherwise it's A's
address that is written to rather than its value, which doesn't make
sense. That's why I said it was asymmetric; the RHS needs an explicit
operator, the LHS doesn't.
No, the address isn't written. The object is written.
The RHS evaluation determines the value currently stored in the object.
The LHS evaluation does not. That's the asymmetry.
In BLISS, the evaluation of the expression A determines the object that
the name A designates. In C, it can either do that *or* it can extract
the value currently stored in that object.
On 29/08/2024 17:06, Ben Bacarisse wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:I can't see what it is you object to in what I wrote. I don't disagree
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment in
pretty much every aspect, but there are extra constraints on the LHS. >>>>>>>> So you use "exactly the same" to mean "exactly the same except for the >>>>>>>> differences".
No, I do mean exactly the same, both in terms of syntax and (in my >>>>>>> implementations, which are likely typical) internal
representation of those
terms.
There are no differences other than where the type system says
your code is
invalid. So are no differences when considering only valid programs. >>>>>>>
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two
previous quotes where it was clear we were talking about C. This is not >>>>>> an honest thing to do. You are arguing for the sake if it, and in a >>>>>> dishonest way too.
It's also valid syntax in C, with a constraint violation that can be >>>>> "caught later on" in an implementation of C, just like in Bart's
language.
Have you taken Bart's bait and are now discussing a narrower context?
The claim that C's assignment is symmetric and what is required on the >>>> two sides is exactly the same is junk. C's assignment has different
syntax on each side, and what is required is even more strict.
In the ISO C grammar for assignment, there is a "unary expression" on
the left and an "assignment expression" on the right. That's just a
particular factoring of the grammar that implementors don't have to
follow, if the correct results are produced.
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side).
Under a parser generator tool we could have a production rule likeA C program that has the wrong syntax (for example x+1) on the left hand
expr '=' expr , where the '=' token has an elsewhere-declared
associativity and precedence.
The basic idea that the same syntactic kind of thing is on both sides of >>> a C assignment (with an additional lvalue constraint) is valid;
it's just not literally true if we are discussing the details of how
ISO C expresses the grammar.
side of an assignment must be rejected. I'm not relying on some fussy
definition about how the syntax is written but making a point that what
is required on each side is not the exactly same thing. Do you really
disagree with that?
So what exactly is different about the LHS and RHS here:
A = A;
A C program that has the wrong syntax (for example x+1) on the left hand
side of an assignment must be rejected.
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason: an '+'
term is not a valid lvalue.
Bart <bc@freeuk.com> writes:
On 29/08/2024 17:06, Ben Bacarisse wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:I can't see what it is you object to in what I wrote. I don't disagree
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
On 29/08/2024 13:35, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I explained that. LHS and RHS can be identical terms for assignment inSo you use "exactly the same" to mean "exactly the same except for the
pretty much every aspect, but there are extra constraints on the LHS.
differences".
No, I do mean exactly the same, both in terms of syntax and (in my >>>>>>>> implementations, which are likely typical) internal
representation of those
terms.
There are no differences other than where the type system says >>>>>>>> your code is
invalid. So are no differences when considering only valid programs. >>>>>>>>
This program in my language:
42 := 42
is valid syntax.
So what? We were talking about assignment in C. You cut the two >>>>>>> previous quotes where it was clear we were talking about C. This is not
an honest thing to do. You are arguing for the sake if it, and in a >>>>>>> dishonest way too.
It's also valid syntax in C, with a constraint violation that can be >>>>>> "caught later on" in an implementation of C, just like in Bart's
language.
Have you taken Bart's bait and are now discussing a narrower context? >>>>>
The claim that C's assignment is symmetric and what is required on the >>>>> two sides is exactly the same is junk. C's assignment has different >>>>> syntax on each side, and what is required is even more strict.
In the ISO C grammar for assignment, there is a "unary expression" on
the left and an "assignment expression" on the right. That's just a
particular factoring of the grammar that implementors don't have to
follow, if the correct results are produced.
with anything you are saying (the "correct result" being to reject a
program that has, syntactically, the wrong thing on the left hand side). >>>
Under a parser generator tool we could have a production rule likeA C program that has the wrong syntax (for example x+1) on the left hand >>> side of an assignment must be rejected. I'm not relying on some fussy
expr '=' expr , where the '=' token has an elsewhere-declared
associativity and precedence.
The basic idea that the same syntactic kind of thing is on both sides of >>>> a C assignment (with an additional lvalue constraint) is valid;
it's just not literally true if we are discussing the details of how
ISO C expresses the grammar.
definition about how the syntax is written but making a point that what
is required on each side is not the exactly same thing. Do you really
disagree with that?
So what exactly is different about the LHS and RHS here:
A = A;
Do you think (or claim) that what is /required/ on each side of an
assignment in C is exactly the same thing? The expression on the LHS is required to be a modifiable lvalue expression. That does not apply to
the expression on right hand side.
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason: an '+'
term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's because what
is required on each side of assignment is not exactly the same thing.
It's a distraction to argue about why each is not valid C as both have
errors that require diagnostic at compile time.
Am 28.08.2024 um 07:39 schrieb Lawrence D'Oliveiro:
The Linux kernel abstractions are very high level. Look at how entirely
different filesystems, even ones originating from entirely different
OSes, can be handled through the common VFS layer.
The distance between the levels of indirection is less than in C++.
They say that Forth is a HLL
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C.
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Have you taken Bart's bait and are now discussing a narrower
context?
The claim that C's assignment is symmetric and what is required on
the two sides is exactly the same is junk. C's assignment has
different syntax on each side, and what is required is even more
strict.
In the ISO C grammar for assignment, there is a "unary expression"
on the left and an "assignment expression" on the right. That's
just a particular factoring of the grammar that implementors don't
have to follow, if the correct results are produced.
Under a parser generator tool we could have a production rule like
expr '=' expr , where the '=' token has an elsewhere-declared
associativity and precedence.
The basic idea that the same syntactic kind of thing is on both
sides of a C assignment (with an additional lvalue constraint) is
valid; it's just not literally true if we are discussing the
details of how ISO C expresses the grammar.
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
On Wed, 28 Aug 2024 08:04:34 +0200, Bonita Montero wrote:
Am 28.08.2024 um 07:39 schrieb Lawrence D'Oliveiro:
The Linux kernel abstractions are very high level. Look at how entirely
different filesystems, even ones originating from entirely different
OSes, can be handled through the common VFS layer.
The distance between the levels of indirection is less than in C++.
Do you have any examples of C++ code that deals with the levels of >indirection in the Linux kernel?
On 8/29/24 19:08, Ben Bacarisse wrote:
...
Actually I don't think I did. I said "reject" and a compiler that says
"this is not C" and then generates a executable is rejecting the code as
far as I am concerned.
How about a compiler that says: "Congratulations on using our extension
to C - program accepted"? Such a compiler could be fully conforming, and
I see no way to describe that as a rejection.
Actually I don't think I did. I said "reject" and a compiler that says
"this is not C" and then generates a executable is rejecting the code as
far as I am concerned.
On 2024-08-30, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
On 8/29/24 19:08, Ben Bacarisse wrote:
...
Actually I don't think I did. I said "reject" and a compiler that says
"this is not C" and then generates a executable is rejecting the code as >>> far as I am concerned.
How about a compiler that says: "Congratulations on using our extension
to C - program accepted"? Such a compiler could be fully conforming, and
I see no way to describe that as a rejection.
Woudln't it hava to correctly look for and process #error directives?
Am 30.08.2024 um 05:21 schrieb Lawrence D'Oliveiro:
Do you have any examples of C++ code that deals with the levels of
indirection in the Linux kernel?
I wanted to say that there are language facilities in C that put a
distance with the interface and the code behind it. In C this doesn't
exist. And there's no encapsulation that helps to manage such abstrac-
tions.
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On Wed, 28 Aug 2024 08:04:34 +0200, Bonita Montero wrote:
Am 28.08.2024 um 07:39 schrieb Lawrence D'Oliveiro:
The Linux kernel abstractions are very high level. Look at how
entirely different filesystems, even ones originating from entirely
different OSes, can be handled through the common VFS layer.
The distance between the levels of indirection is less than in C++.
Do you have any examples of C++ code that deals with the levels of >>indirection in the Linux kernel?
The SVR4 VFS layer (which linux adopted) is ideally suited to using C++ derived classes. As is the network stack.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
The second line is syntactically well-formed. The first line is
not.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
However, the compilers I've tried produce the same diagnostic (not a
syntax error message) for both. Probably they use a tweaked grammar
that allows more a general expression as the LHS of an assignment,
and catch errors later in semantic analysis, for the purpose of
producing diagnostics that are easier to understand. It's obvious
that in `x + 1 = y`, the programmer (probably) intended `x + 1`
to be the LHS of an assignment. These compilers (I tried gcc,
clang, and tcc) are clever enough to recognize that.
Ben Bacarisse <ben@bsb.me.uk> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 8/29/24 12:06, Ben Bacarisse wrote:
...
I can't see what it is you object to in what I wrote. I don't
disagree with anything you are saying (the "correct result" being
to reject a program that has, syntactically, the wrong thing on
the left hand side).
No - the only requirement is that a diagnostic be produced. A
fully conforming implementation of C is allowed to accept such
code and then generate an executable; if you choose to execute
the executable, the behavior is undefined.
Sorry, I used a term incorrectly. To put it informally, you must
be told that "this is not C". Not everything is C even if a C
compiler will accept FORTRAN code as an extension.
Actually I don't think I did. I said "reject" and a compiler that
says "this is not C" and then generates a executable is rejecting
the code as far as I am concerned.
On 8/29/24 19:08, Ben Bacarisse wrote:
...
Actually I don't think I did. I said "reject" and a compiler
that says "this is not C" and then generates a executable is
rejecting the code as far as I am concerned.
How about a compiler that says: "Congratulations on using our
extension to C - program accepted"?
On 8/26/24 03:54, Michael S wrote:
On Sun, 25 Aug 2024 17:48:14 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
...
It's been amusing reading a discussion of which languages are or
are not high level, without anyone offering a definition of what
the term means. Wikipedia says, roughly, that a high-level
language is one that doesn't provide machine-level access (and IMO
that is a reasonable characterization).
I don't like this definition. IMHO, what language does have is at
least as important as what it does not have for the purpose of
estimating its level.
That's not a particularly useful response. [...]
One principle that should be kept in mind when you're defining a
term whose definition is currently unclear, is to decide what
statements you want to make about things described by that term.
On 2024-08-30, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:...
However, the compilers I've tried produce the same diagnostic (not a
syntax error message) for both. Probably they use a tweaked grammar
that allows more a general expression as the LHS of an assignment,
and catch errors later in semantic analysis, for the purpose of
producing diagnostics that are easier to understand. It's obvious
that in `x + 1 = y`, the programmer (probably) intended `x + 1`
to be the LHS of an assignment. These compilers (I tried gcc,
clang, and tcc) are clever enough to recognize that.
A standard operator precedence parsing algorithm such as Shunting Yard
cannot help but parse that.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
The second line is syntactically well-formed. The first line is
not.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
On 30/08/2024 21:41, Keith Thompson wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
The second line is syntactically well-formed. The first line is
not.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
AFAICT both terms are parsed the same way.
Given this:
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both cases. I
can't easily get the AST created by gcc. But the parsing of 'x + y = z'
has to be as one of these two:
x + (y = z);
(x + y) = z;
I'm surprised that the experts here are unsure about it.
On 30/08/2024 21:41, Keith Thompson wrote:...
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
AFAICT both terms are parsed the same way.
Given this:
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both cases. I
can't easily get the AST created by gcc. But the parsing of 'x + y = z'
has to be as one of these two:
x + (y = z);
(x + y) = z;
I'm surprised that the experts here are unsure about it.
On 30/08/2024 21:41, Keith Thompson wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently
are both valid syntax in C. It will fail for a different reason:
an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
The second line is syntactically well-formed. The first line is
not.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
AFAICT both terms are parsed the same way.
Given this:
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both
cases.
Am 31.08.2024 um 02:01 schrieb Lawrence D'Oliveiro:
On Fri, 30 Aug 2024 10:43:10 +0200, Bonita Montero wrote:
Am 30.08.2024 um 05:21 schrieb Lawrence D'Oliveiro:
Do you have any examples of C++ code that deals with the levels of
indirection in the Linux kernel?
I wanted to say that there are language facilities in C that put a
distance with the interface and the code behind it. In C this doesn't
exist. And there's no encapsulation that helps to manage such abstrac-
tions.
That’s a theoretical argument. ...
No, that's totally practical.
I use those facilties every 5min while programmming.
Bart <bc@freeuk.com> writes:
[...]
Given this:[...]
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both cases.
If that's the case (and I don't doubt that it is), then your compiler is
not following the grammar specified by the ISO C standard. Since
`x + y` is not a unary-expression, `x + y = z` is not a syntactically
valid assignment expression.
A parser that strictly follows the ISO C grammar would reject
(diagnose, flag, whatever) `x + y = z;` just as it would reject `x = y +;`.
This is an observation, not a complaint. It doesn't imply that your
compiler is non-conforming or buggy. A parser that doesn't strictly
follow the ISO C grammar could still be part of a conforming compiler.
Bart <bc@freeuk.com> writes:[...]
I can also say that the C grammar is buggy:
assignment-expression:
conditional-expression
unary-expression asssignment-operator assignment-expression
When attempting to parse an assignment-expression, do you go for a
conditional-expression or unary-expression?
The latter is a subset of the former. If you go for a
conditional-expression and find that an assignment-operator
follows, now you have to perform some analysis on the LHS to see
if that conditional-expression contains only a unary-expression.
[...]
[...] I'm skeptical that the C grammar is buggy. [...]
On 31/08/2024 23:31, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]
Given this:[...]
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both cases.
If that's the case (and I don't doubt that it is), then your compiler is
not following the grammar specified by the ISO C standard. Since
`x + y` is not a unary-expression, `x + y = z` is not a syntactically
valid assignment expression.
Yet no compiler out of the dozen I tried reported a syntax error (except SDCC).
So what AST is produced by gcc, if any?
Most compilers, including mine, complain that an lvalue is expected.
I can also say that the C grammar is buggy:
assignment-expression:
conditional-expression
unary-expression asssignment-operator assignment-expression
When attempting to parse an assignment-expression, do you go for a conditional-expression or unary-expression?
Strachey coined the
terms Lvalue and Rvalue to describe the kind of value passed by
reference and value parameters, respectively.
Bart <bc@freeuk.com> writes:
On 30/08/2024 21:41, Keith Thompson wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:
Bart <bc@freeuk.com> writes:
I think that these (with x, y having compatible scalar types):
x + 1 = y;
(x + 1) = y; // in case above was parsed differently >>>>>>>
are both valid syntax in C. It will fail for a different reason: >>>>>>> an '+' term is not a valid lvalue.
The compiler must tell you that neither is valid C. That's
because what is required on each side of assignment is not
exactly the same thing. It's a distraction to argue about why
each is not valid C as both have errors that require diagnostic
at compile time.
Bart is only saying that it's valid syntax, not that it's valid C.
According to the ISO C syntax (not taking into account contraints,
which are not syntax) that view is justified.
The second line is syntactically well-formed. The first line is
not.
Right, because the LHS of an assignment is a unary-expression.
`(x + 1)` can be parsed as a unary-expression, but `x + 1` cannot.
AFAICT both terms are parsed the same way.
Given this:
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both
cases.
To understand why they are different, try drawing parse trees
rather than abstract syntax trees.
https://en.wikipedia.org/wiki/Parse_tree
On 31/08/2024 23:10, Tim Rentsch wrote:[...]
Bart <bc@freeuk.com> writes:
Given this:
x + y = z;
(x + y) = z;
My compiler produces the same AST for the LHS of '=' in both
cases.
To understand why they are different, try drawing parse trees
rather than abstract syntax trees.
https://en.wikipedia.org/wiki/Parse_tree
Yeah, the CST differs in retaining the parentheses. But I can
already see that from the source code.
For normal compilation, that information is redundant.
On Fri, 30 Aug 2024 14:38:03 GMT, Scott Lurndal wrote:
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On Wed, 28 Aug 2024 08:04:34 +0200, Bonita Montero wrote:
Am 28.08.2024 um 07:39 schrieb Lawrence D'Oliveiro:
The Linux kernel abstractions are very high level. Look at how
entirely different filesystems, even ones originating from entirely
different OSes, can be handled through the common VFS layer.
The distance between the levels of indirection is less than in C++.
Do you have any examples of C++ code that deals with the levels of >>>indirection in the Linux kernel?
The SVR4 VFS layer (which linux adopted) is ideally suited to using C++
derived classes. As is the network stack.
Can you point to any examples of an actual C++-based implementation of
same?
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On Fri, 30 Aug 2024 14:38:03 GMT, Scott Lurndal wrote:
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On Wed, 28 Aug 2024 08:04:34 +0200, Bonita Montero wrote:
Am 28.08.2024 um 07:39 schrieb Lawrence D'Oliveiro:
The Linux kernel abstractions are very high level. Look at how
entirely different filesystems, even ones originating from entirely >>>>>> different OSes, can be handled through the common VFS layer.
The distance between the levels of indirection is less than in C++.
Do you have any examples of C++ code that deals with the levels of >>>>indirection in the Linux kernel?
The SVR4 VFS layer (which linux adopted) is ideally suited to using C++
derived classes. As is the network stack.
Can you point to any examples of an actual C++-based implementation of >>same?
Yes. They are, of course proprietary and thus not publically
available.
That's always been your problem - you think your experiences are
all that exist.
On 31/08/2024 23:31, Keith Thompson wrote:...
Bart <bc@freeuk.com> writes:
I can also say that the C grammar is buggy:
assignment-expression:
conditional-expression
unary-expression asssignment-operator assignment-expression
When attempting to parse an assignment-expression, do you go for a conditional-expression or unary-expression?
The latter is a subset of the former. If you go for a
conditional-expression and find that an assignment-operator follows,
now you have to perform some analysis on the LHS to see if that conditional-expression contains only a unary-expression.
Any invalid syntax condition that can be removed using parentheses
is not worth enforcing at the parse level. If it's wrong to
assign to x + 1, you also need to diagnose when it's (x + 1).
It's better to have a single rule which catches both.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
Any invalid syntax condition that can be removed using parentheses
is not worth enforcing at the parse level. If it's wrong to
assign to x + 1, you also need to diagnose when it's (x + 1).
It's better to have a single rule which catches both.
If you want to think that you are free to do so. But the
statement is nothing more than one person's opinion.
Tim, are you under the impression that we need help figuring out
whether something is an opinion or not? I don't believe we do.
On 2024-09-01, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:...
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
If you want to think that you are free to do so. But the
statement is nothing more than one person's opinion.
Tim, are you under the impression that we need help figuring out
whether something is an opinion or not? I don't believe we do.
The point obviously not that it's an opinion, but that it's only one
person's opinion; i.e. that I have an opinion not shared by anyone, insinuating that it's extremely weird or poorly considered (or else so brilliant that its blinding `wisdom is inaccessible to anyone else).
On 2024-08-31, Bart <bc@freeuk.com> wrote:[...]
I can also say that the C grammar is buggy:
assignment-expression:
conditional-expression
unary-expression asssignment-operator assignment-expression
I second that. If I had energy and motivation for that sort of
thing, I would submit a defect report.
There is no value (no pun intended) in constraining the left side
of an assignment to just that class of expression that might
produce a modifiable lvalue, since that constraint must be checked regardless.
It creates an gratuitous inconsistency between the formal ISO C
grammar and the intuitive precedence model for expressions that
everyone understands. Even the K&R2 book has a precedence table
(P. p53?).
When attempting to parse an assignment-expression, do you go for
a conditional-expression or unary-expression?
If using a parser generator tool, like one of the Yacc
derivatives, I would just make it:
expr : ... /* numerous rules */
| expr '=' expr { ... }
| ... /* numerous rules */
;
using %left and %right declarations to set up the operator
precedences and associativities.
Anything you do differently from a specification, though, creates
risk. It takes extra work to show that what you have is
equivalent.
On 2024-09-01, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
Any invalid syntax condition that can be removed using parentheses
is not worth enforcing at the parse level. If it's wrong to
assign to x + 1, you also need to diagnose when it's (x + 1).
It's better to have a single rule which catches both.
If you want to think that you are free to do so. But the
statement is nothing more than one person's opinion.
Tim, are you under the impression that we need help figuring out
whether something is an opinion or not? I don't believe we do.
The point obviously not that it's an opinion, but that it's only one
person's opinion; i.e. that I have an opinion not shared by anyone, insinuating that it's extremely weird or poorly considered [...]
On 9/1/24 14:47, Kaz Kylheku wrote:
On 2024-09-01, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
...
If you want to think that you are free to do so. But the
statement is nothing more than one person's opinion.
Tim, are you under the impression that we need help figuring out
whether something is an opinion or not? I don't believe we do.
The point obviously not that it's an opinion, but that it's only one
person's opinion; i.e. that I have an opinion not shared by anyone,
insinuating that it's extremely weird or poorly considered (or else so
brilliant that its blinding `wisdom is inaccessible to anyone else).
He didn't say that it was "only" one person's opinion. I don't think he
was implying that your opinion is unique. [...]
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
Any invalid syntax condition that can be removed using parentheses
is not worth enforcing at the parse level. If it's wrong to
assign to x + 1, you also need to diagnose when it's (x + 1).
It's better to have a single rule which catches both.
If you want to think that you are free to do so. But the
statement is nothing more than one person's opinion.
Tim, are you under the impression that we need help figuring out
whether something is an opinion or not? I don't believe we do.
This could be an opportunity to let us know what your opinion is,
and perhaps even support it. That might be interesting.
scott@slp53.sl.home (Scott Lurndal) writes:
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
On Fri, 30 Aug 2024 14:38:03 GMT, Scott Lurndal wrote:
Lawrence D'Oliveiro <ldo@nz.invalid> writes:
Do you have any examples of C++ code that deals with the levels of >>>>>indirection in the Linux kernel?
The SVR4 VFS layer (which linux adopted) is ideally suited to using
C++ derived classes. As is the network stack.
Can you point to any examples of an actual C++-based implementation of >>>same?
Here's one:
https://en.wikipedia.org/wiki/Chorus_Syst%C3%A8mes_SA
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:...
Do you think (or claim) that what is /required/ on each side of an
assignment in C is exactly the same thing? The expression on the LHS is
required to be a modifiable lvalue expression. That does not apply to
the expression on right hand side.
"modifiable lvalue" is a semantic attribute which depends on type
and qualification. An array is an lvalue, but not modifiable.
A const-qualified expression is also not a modififiable lvalue.
Bart is insisting that these attributes are not a matter of syntax.
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:...
Do you think (or claim) that what is /required/ on each side of an
assignment in C is exactly the same thing? The expression on the LHS is >>> required to be a modifiable lvalue expression. That does not apply to
the expression on right hand side.
"modifiable lvalue" is a semantic attribute which depends on type
and qualification. An array is an lvalue, but not modifiable.
A const-qualified expression is also not a modififiable lvalue.
Bart is insisting that these attributes are not a matter of syntax.
Your intervention derailed the discussion into one of syntax. Bart then simply stopped talking about his original claim. Way back I pointed
out that:
|| What is needed on the two sides is not the same.
And he replied
| I would argue that it is exactly the same.
He did, later, say that is was "exactly the same" except for the
differences but then went back to "I do mean exactly the same".
I explained that. LHS and RHS can be identical terms for assignment in pretty much every aspect, but there are extra constraints on the LHS.
So you use "exactly the same" to mean "exactly the same except for the differences".
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of
those terms.
...So are no differences when considering only valid programs.
On 02/09/2024 13:03, Ben Bacarisse wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:...
Your intervention derailed the discussion into one of syntax. Bart thenDo you think (or claim) that what is /required/ on each side of an
assignment in C is exactly the same thing? The expression on the LHS is >>>> required to be a modifiable lvalue expression. That does not apply to >>>> the expression on right hand side.
"modifiable lvalue" is a semantic attribute which depends on type
and qualification. An array is an lvalue, but not modifiable.
A const-qualified expression is also not a modififiable lvalue.
Bart is insisting that these attributes are not a matter of syntax.
simply stopped talking about his original claim. Way back I pointed
out that:
|| What is needed on the two sides is not the same.
And he replied
| I would argue that it is exactly the same.
He did, later, say that is was "exactly the same" except for the
differences but then went back to "I do mean exactly the same".
I said this:
I explained that. LHS and RHS can be identical terms for assignment in
pretty much every aspect, but there are extra constraints on the LHS.
You then sarcastically suggested:
So you use "exactly the same" to mean "exactly the same except for the
differences".
I then clarified:
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of
those terms.
...So are no differences when considering only valid programs.
Bart <bc@freeuk.com> writes:
On 02/09/2024 13:03, Ben Bacarisse wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2024-08-29, Ben Bacarisse <ben@bsb.me.uk> wrote:...
Your intervention derailed the discussion into one of syntax. Bart then >>> simply stopped talking about his original claim. Way back I pointedDo you think (or claim) that what is /required/ on each side of an
assignment in C is exactly the same thing? The expression on the LHS is >>>>> required to be a modifiable lvalue expression. That does not apply to >>>>> the expression on right hand side.
"modifiable lvalue" is a semantic attribute which depends on type
and qualification. An array is an lvalue, but not modifiable.
A const-qualified expression is also not a modififiable lvalue.
Bart is insisting that these attributes are not a matter of syntax.
out that:
|| What is needed on the two sides is not the same.
And he replied
| I would argue that it is exactly the same.
He did, later, say that is was "exactly the same" except for the
differences but then went back to "I do mean exactly the same".
I said this:
I explained that. LHS and RHS can be identical terms for assignment in
pretty much every aspect, but there are extra constraints on the LHS.
You then sarcastically suggested:
So you use "exactly the same" to mean "exactly the same except for the
differences".
I then clarified:
No, I do mean exactly the same, both in terms of syntax and (in my
implementations, which are likely typical) internal representation of
those terms.
...So are no differences when considering only valid programs.
I wonder what it was you were really objecting to in the original remark
that I made. Since ignoring the differences in what is required on the
LHS and RHS all result in invalid programs your summary is (to a first approximation) correct, but it does not render mine wrong in any
interesting way.
I note that you have, again, indulged in strategic snipping. The "..."
was "There are no differences other than where the type system says your
code is invalid.". What is it about the type system of C that makes
int main(void) {
extern char *p;
*p = 0;
}
invalid? Because sometimes it is,
depending on what p is in some other
translation unit. Are you using your own meaning for "type system"? If
so what is it?
And as for your remarks about typical implementations, does your C
parser /really/ accept an assignment expression on both sides of an = operator? What does that even look like in the code? I have written
one C parser, contributed to one other and (over the years) examined at
least two more, and none of them do what you seem to be suggesting is typical.
Bart <bc@freeuk.com> writes:
[...]
But this is venturing away from the question of whether the left and
right sides of an assignment are compatible, or the same, or
symmetric.
Obviously, one side is written to and the other is read; the RHS can
also contain a wider range of terms than the left side.
But usually what can be legally on the left side on an assignment, can
also written on the right, and with the same syntax, and the same
levels of indirection.
Yes, but what can legally be on the right side of an assignment very
often cannot be written on the left. I don't call that "symmetric".
Bart <bc@freeuk.com> writes:
On 02/09/2024 23:31, Keith Thompson wrote:[...]
Yes, but what can legally be on the right side of an assignment very
often cannot be written on the left. I don't call that "symmetric".
The symmetry is about when you /do/ legally have the same thing either
side of '='. That is in contrast to BLISS where the RHS needs an
explicit deref symbol, but the LHS doesn't.
Thank you for clarifying what you mean by "symmetric".
I won't waste any more time debating it.
And as for your remarks about typical implementations, does your C
parser /really/ accept an assignment expression on both sides of
an = operator? What does that even look like in the code? I have
written one C parser, contributed to one other and (over the
years) examined at least two more, and none of them do what you
seem to be suggesting is typical.
But this is venturing away from the question of whether the left
and right sides of an assignment are compatible, or the same, or
symmetric.
Obviously, one side is written to and the other is read; the RHS
can also contain a wider range of terms than the left side.
But usually what can be legally on the left side on an assignment,
can also written on the right, and with the same syntax, and the
same levels of indirection.
Ben Bacarisse <ben@bsb.me.uk> writes:
[...]
And as for your remarks about typical implementations, does your C
parser /really/ accept an assignment expression on both sides of
an = operator? What does that even look like in the code? I have
written one C parser, contributed to one other and (over the
years) examined at least two more, and none of them do what you
seem to be suggesting is typical.
It wouldn't be surprising to see a parser written so it would
accept (syntactically) a superset of the well-formed inputs
allowed by the language grammar. Any parses not allowed by the
grammar could then be flagged as erroneous in a later semantics
pass.
One reason to do this is to simplify error recovery in the face
of syntax errors. It's much easier to recover from a "correct"
parse than from one that looks hopelessly lost.
I'm not making any claim that such an approach is typical. On
the other hand it does seem to fit with some of the diagnostics
given by gcc for inputs that are syntactically ill-formed.
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A' is
also valid, there is a hidden mismatch in indirection levels between
left and right. It is asymmetric while in C it is symmetric, although
seem to disagree on that latter point.)
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A' is
also valid, there is a hidden mismatch in indirection levels between
left and right. It is asymmetric while in C it is symmetric, although
seem to disagree on that latter point.)
You seem to miss the point that assigment operator is fundamentally assymetic.
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A' is
also valid, there is a hidden mismatch in indirection levels between
left and right. It is asymmetric while in C it is symmetric, although
seem to disagree on that latter point.)
You seem to miss the point that assigment operator is fundamentally assymetic.
This is quite visible at low level, where typical
machine has 'store' instruction. Store takes address (memory location)
as left argument, but a _value_ as right argument. Your example
introdices fake symmetry, you ealuate right hand side using
a load ant this may look symmetric with store.
But even here
there is asymetry, which is better visible with naive compiler.
You may get code like
compute addres of A
load
compute address of A
store
The last step implement '=', the second 'compute address' corresponds
to A on the left had side. First 'compute address' corresponds to
A on the right hand side. Now you see that beside address computation
there is also load corresponding to A on the right hand side.
So clearly in most languages treatment of sides is assymetric:
extra loads are inserted due to 'lvalue convertion'.
To put in more general context: early success of C was related
to exposing address computations, so that programmers could
do optimization by hand (and consequently non-optimizing compiler
could produce reasonably fast object code). This has a cost:
need for explicit point dereferences not needed in other langiages.
Bliss went slightly further and requires explicit derefernces
to get values of variables. My point is that this is logical
regardless if you like it or not.
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:...
You seem to miss the point that assigment operator is fundamentally
assymetic.
Both sides of an assignment can be complex expressions that designate
an object (though the right side need not).
On 9/5/24 12:54, Kaz Kylheku wrote:
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:...
You seem to miss the point that assigment operator is fundamentally
assymetic.
Both sides of an assignment can be complex expressions that designate
an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
On 05/09/2024 22:37, James Kuyper wrote:
On 9/5/24 12:54, Kaz Kylheku wrote:
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:...
You seem to miss the point that assigment operator is
fundamentally assymetic.
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
That means that for you, there is no interesting difference (using my
example of assigning A to itself) in a language where you write 'A =
A', and one where you write 'A = .A'.
(I'd be interested in how, in the latter language, you'd write the
equivalent of 'A = A = A' in C, since the middle term is both on the
left of '=', and on the right!)
On 05/09/2024 16:21, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while 'A = A' is >>> also valid, there is a hidden mismatch in indirection levels between
left and right. It is asymmetric while in C it is symmetric, although
seem to disagree on that latter point.)
You seem to miss the point that assigment operator is fundamentally
assymetic.
If you've followed the subthread then you will know that nobody disputes
that assignment reads from side of '=' and writes to the other.
The symmetry is to do with syntax when the same term appears on both
sides of '=', the type associated with each side, and, typically, the internal representations too.
Clearly the '=' operation is not reversible (or cummutative), as binary
'+' might be, but not binary '-'. That is not what I'm claiming.
This is quite visible at low level, where typical
machine has 'store' instruction. Store takes address (memory location)
as left argument, but a _value_ as right argument. Your example
introdices fake symmetry, you ealuate right hand side using
a load ant this may look symmetric with store.
Low level can reveal symmetry too. If 'A' is stored in a register 'Ra',
then copying A to itself can be done like this on x64:
mov Ra, Ra
(Whether data movement is RTL or LTR depends on the assembly syntax, but
in this example it doesn't matter.)
If A is in memory then it could be the same on 2-address architectures:
mov [A], [A]
but more typically it needs two instructions (here using RTL):
mov R, [A]
mov [A], R
Here, [A] appears in both instructions, it means the same thing, and
refers to the same location. Only the position (left vs. right operand, exactly the same as in A = A) tells you if it's reading or writing.
But even here
there is asymetry, which is better visible with naive compiler.
You may get code like
compute addres of A
load
compute address of A
store
The last step implement '=', the second 'compute address' corresponds
to A on the left had side. First 'compute address' corresponds to
A on the right hand side. Now you see that beside address computation
there is also load corresponding to A on the right hand side.
So clearly in most languages treatment of sides is assymetric:
extra loads are inserted due to 'lvalue convertion'.
There is a Load on one operand and a balancing Store on the other. Two
loads or two stores would not make sense here.
If you want to go to a lower level, look at how a simple microprocessor works. It will generate a /WR signal on memory accesses that tells a RAM device whether to use the data bus as input or output.
Note that Load and Store can also be considered symmetric: each Load
reads data from somewhere and writes it somewhere else. Just like Store
does. So some instruction sets use the same mnemonic for both.
To put in more general context: early success of C was related
to exposing address computations, so that programmers could
do optimization by hand (and consequently non-optimizing compiler
could produce reasonably fast object code). This has a cost:
need for explicit point dereferences not needed in other langiages.
Bliss went slightly further and requires explicit derefernces
to get values of variables. My point is that this is logical
regardless if you like it or not.
And /my/ point was that in virtually every HLL, that dereference to turn
a variable's address, denoted by its name, into either a read or write
access of its value, is implicit.
On 05/09/2024 16:21, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while
'A = A' is also valid, there is a hidden mismatch in indirection
levels between left and right. It is asymmetric while in C it
is symmetric, although seem to disagree on that latter point.)
You seem to miss the point that assigment operator is
fundamentally assymetic.
If you've followed the subthread then you will know that nobody
disputes that assignment reads from side of '=' and writes to the
other.
The symmetry is to do with syntax when the same term appears on
both sides of '=', the type associated with each side, and,
typically, the internal representations too.
Bart <bc@freeuk.com> wrote:
If you've followed the subthread then you will know that nobody disputes
that assignment reads from side of '=' and writes to the other.
I dispute this and I think that to same degree several other folks too. Assgmenet _does not read_, it "only" writes. Assigment get two
parameters which are treated in different way. Imagine that you
are programming in a language like C, but are forbidden to use
assignment operator. But fortunately you have C "function"
'assign' with prototype:
void assign(int * p, int v);
Instead of writing
A = B
you need to write
assign(&A, B)
Of course, in real life nobody is going to force you to anything,
but except for fact that in C assignment has value the 'assign'
function is doing the same thing as '=' operator. And you can
see that it is asymetric: first agrument is an addres and right
is a value.
If A is in memory then it could be the same on 2-address architectures:
mov [A], [A]
but more typically it needs two instructions (here using RTL):
mov R, [A]
mov [A], R
Here, [A] appears in both instructions, it means the same thing, and
refers to the same location. Only the position (left vs. right operand,
exactly the same as in A = A) tells you if it's reading or writing.
You somewhat miss fact that "A = B" has 3 parts, that is "A", "=", and "B". The second 'mov' instruction came from "=", the first 'mov' is extra.
So instructions look symmetric, but clearly assigment part is asumetric.
There is a Load on one operand and a balancing Store on the other. Two
loads or two stores would not make sense here.
Again: only store comes from assignment. This is clearly visible
if instead of misleading "A = A" you take "A = B" and replace
'B' by various things. Assigment part (store instruction) stays
the same, compution of value changes. In
A = c + d
you get two load (for c and d) and then addition. To put it
differently, you have
compute value of B
compute address of A
store
Note that Load and Store can also be considered symmetric: each Load
reads data from somewhere and writes it somewhere else. Just like Store
does. So some instruction sets use the same mnemonic for both.
Concerning instruction, sure. But load is not an assignment.
It may look so in simple misleading cases.
But even if 'A' is
allocated to register and you translate whole "A = B" to single
load, the load computes value of 'B' and if the result is in
correct register the assigment proper can be optimised to no
operation.
Well, you need /something/ to denote whether you are reading or writingAnd /my/ point was that in virtually every HLL, that dereference to turn
a variable's address, denoted by its name, into either a read or write
access of its value, is implicit.
I partially agree. Normal case is that write access is explicit
(for example via '=' in C) and it simly takes variable address. Only
read access in implicit.
Bart <bc@freeuk.com> writes:
On 05/09/2024 16:21, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while
'A = A' is also valid, there is a hidden mismatch in indirection
levels between left and right. It is asymmetric while in C it
is symmetric, although seem to disagree on that latter point.)
You seem to miss the point that assigment operator is
fundamentally assymetic.
If you've followed the subthread then you will know that nobody
disputes that assignment reads from side of '=' and writes to the
other.
The symmetry is to do with syntax when the same term appears on
both sides of '=', the type associated with each side, and,
typically, the internal representations too.
Maybe it would help if you would stop thinking in terms of the
word symmetry (clearly assignment is not symmetrical) and instead
think about consistency.
In C, the meaning of an identifier or object-locating expression
depends on where it is in the syntax tree. In some places it
means the address of the object; in other places it means the
contents of whatever is stored in the object.
Those meanings
are very different; among other things, they have different
types (if one type is 'int' the other is 'int *').
In Bliss, by contrast, the meaning of an identifier is the same
no matter where it appears in the syntax tree: it always means
the address of the object. The meaning is independent of where
the term appears in the input, which is to say the meaning is
consistent from place to place.
In C the meaning is not consistent - in some places it means the
address, in other places whatever is stored at the address.
Considering the point of view of a compiler writer, it's easier
to write a compiler for Bliss than for C. In Bliss, upon seeing
an identifier, always simply put its address in a register. If
an object's value needs to be loaded, there will be a '.' to take
the address produced by the sub-expression and fetch the word
stored at that address. On the other hand, in C, upon seeing an
identifier, the compiler needs to consider the context of where
the identifier appears:
On Fri, 6 Sep 2024 10:35:16 +0100
Bart <bc@freeuk.com> wrote:
On 05/09/2024 22:37, James Kuyper wrote:
On 9/5/24 12:54, Kaz Kylheku wrote:
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:
...
You seem to miss the point that assigment operator is
fundamentally assymetic.
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
That means that for you, there is no interesting difference (using my
example of assigning A to itself) in a language where you write 'A =
A', and one where you write 'A = .A'.
(I'd be interested in how, in the latter language, you'd write the
equivalent of 'A = A = A' in C, since the middle term is both on the
left of '=', and on the right!)
The point is that in BLISS everithing that is legal on the right side of asignment is also legal on the left side.
I don't know if the point is generally true. In particular, if BLISS supports floatig point, what is meaning of floating point on the left
side?
On 05/09/2024 22:37, James Kuyper wrote:...
On 9/5/24 12:54, Kaz Kylheku wrote:
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
On Fri, 6 Sep 2024 10:35:16 +0100
Bart <bc@freeuk.com> wrote:
On 05/09/2024 22:37, James Kuyper wrote:...
On 9/5/24 12:54, Kaz Kylheku wrote:
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
Anything can be considered symmetric, if you ignore all the aspects of
it that are asymmetric. As a result, calling something symmetric for
that reason isn't worth commenting on.
On Fri, 6 Sep 2024 10:35:16 +0100
Bart <bc@freeuk.com> wrote:
On 05/09/2024 22:37, James Kuyper wrote:...
On 9/5/24 12:54, Kaz Kylheku wrote:
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
Anything can be considered symmetric, if you ignore all the aspects of
it that are asymmetric. As a result, calling something symmetric for
that reason isn't worth commenting on.
A more useful way of describing what you're commenting on is not to
falsely claim that assignment in general is symmetric, but rather that
the particular assignment you're interest in is symmetric. And it's only symmetric syntactically; the associated semantics are profoundly asymmetric.
And it's onlyasymmetric.
symmetric syntactically; the associated semantics are profoundly
On 06/09/2024 11:19, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
If you've followed the subthread then you will know that nobody disputes >>> that assignment reads from side of '=' and writes to the other.
I dispute this and I think that to same degree several other folks too.
Assgmenet _does not read_, it "only" writes. Assigment get two
parameters which are treated in different way. Imagine that you
are programming in a language like C, but are forbidden to use
assignment operator. But fortunately you have C "function"
'assign' with prototype:
void assign(int * p, int v);
Instead of writing
A = B
you need to write
assign(&A, B)
Of course, in real life nobody is going to force you to anything,
but except for fact that in C assignment has value the 'assign'
function is doing the same thing as '=' operator. And you can
see that it is asymetric: first agrument is an addres and right
is a value.
If you have to use a function, yes. Because you've introduced an
artificial split in Where and When those dereferences are done.
With A=B they can be done at about the same time and the same place.
With ASSIGN(), the B dereference is done at the call-site; the A
deference is done inside ASSIGN(), so you are obliged to pass an
explicit reference to A. While B has already been dereferenced and you
have its value to pass.
(You can balance it out by by requiring ASSIGN(&A, &B)!)
If A is in memory then it could be the same on 2-address architectures:
mov [A], [A]
but more typically it needs two instructions (here using RTL):
mov R, [A]
mov [A], R
Here, [A] appears in both instructions, it means the same thing, and
refers to the same location. Only the position (left vs. right operand,
exactly the same as in A = A) tells you if it's reading or writing.
You somewhat miss fact that "A = B" has 3 parts, that is "A", "=", and "B". >> The second 'mov' instruction came from "=", the first 'mov' is extra.
So instructions look symmetric, but clearly assigment part is asumetric.
Here is A:=B from my HLLs in one of my ILs (intermediate language):
load B
store A
That was stack-based; here it is in 3-address-code IL:
a := b # more formally, 'move a, b'
This is it in dynamic byte-code:
push B
pop A
In every case, both A and B operands have the same rank and the same
levels of indirection.
There is a Load on one operand and a balancing Store on the other. Two
loads or two stores would not make sense here.
Again: only store comes from assignment. This is clearly visible
if instead of misleading "A = A" you take "A = B" and replace
'B' by various things. Assigment part (store instruction) stays
the same, compution of value changes. In
A = c + d
This has been covered. The syntactical symmetry is that whatever you
have on the LHS, you can write the same thing on the RHS:
A[i+1].m = A[i+1].m
Obviously, you can have RHS terms that cannot appear on the left, like
'42', but that's usually due to separate constraints of the language.
you get two load (for c and d) and then addition. To put it
differently, you have
compute value of B
compute address of A
store
Why don't you need to compute the address of B?
Why don't you need to
load the value of B?
It is more like this:
compute address of B
load value of B via that address to some temporary location
compute address of A
store new value of A via that address
The only asymmetry in all my examples has been between Load/Store;
Push/Pop; or positional as in Left/Right.
The mechanism for EITHER reading or writing the value of an object via
its reference is the same; only the direction of data movement is the parameter.
Note that Load and Store can also be considered symmetric: each Load
reads data from somewhere and writes it somewhere else. Just like Store
does. So some instruction sets use the same mnemonic for both.
Concerning instruction, sure. But load is not an assignment.
It may look so in simple misleading cases.
In my PUSH B example from interpreted code, it will read B from memory,
and write it to a stack (also in memory). POP A will read from the
stack, and write to memory.
So both operations are really memory-to-memory.
But even if 'A' is
allocated to register and you translate whole "A = B" to single
load, the load computes value of 'B' and if the result is in
correct register the assigment proper can be optimised to no
operation.
I've done this stuff at the chip level (writing into an 8-bit latch for example, then reading from it); it's going to take a lot to convince me
that this is anything much different from a read/write or direction flag!
In more complicated cases in languages, then some asymmetry does come
up. For example, suppose C allowed this (my language allows the equivalent):
(c ? a : b) = x;
So this assigns to either a or b depending on c. My implementation effectively turns it into this:
*(c ? &a : &b) = x;
So using explicit references and derefs. However, that is internal. The symmetry still exists in the syntax:
(c ? a : b) = (c ? a : b);
I can also say that the C grammar is buggy:
assignment-expression:
conditional-expression
unary-expression asssignment-operator assignment-expression
When attempting to parse an assignment-expression, do you go for a conditional-expression or unary-expression?
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.
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.
Of course, if you don not minds slowdowns due to stack use, then
stack machine leads to very simple implemantaion.
Best Forth compilers
have sophisticated code to track stack use and replace it by
use of registers. Other Forth compilers just accept slowness.
Obviously, you can have RHS terms that cannot appear on the left, like
'42', but that's usually due to separate constraints of the language.
Well, logically you can not change value of a number, so you can
not assign to a number, that is very fundamental.
You could
try to define
x + 1 = y
as solving equation for x, that quickly runs into trouble due to
equations which are no solutions or multiple solutions.
Why don't you need to compute the address of B?
Well, B may have no address. In case when B is variable computing
its address is part of computation of its value. In general,
computing value of B need computing addresses of all variables
contained in B.
Why don't you need to
load the value of B?
"compute value" means putting result in place which is available
to subsequent operations, so logically no extra load is needed.
And for variables "compute value" includes loading them.
It is more like this:
compute address of B
load value of B via that address to some temporary location
compute address of A
store new value of A via that address
The point is that last part (that is store instruction) logically
does not change when you vary A and B. Only this part corresponds to assignment. The first to lines logically form a whole, that
is "compute B". And when you vary B you may get quite different split
of "compute B" into part. Moreover, logically "compute value of B"
and "compute address of A" are completely independent.
(c ? a : b) = (c ? a : b);
As noticed, people prefer symmetric notation, so most languages
make it "symmetric looking". But if you dig deeper there is
fundametal asymetry.
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.
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.
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.
What started the subthread was the question of which HLL goes between
ASM and C (since someone suggested that C was mid-level).
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.
[...]
[...] in a language where you write 'A = A',
and one where you write 'A = .A'.
(I'd be interested in how, in the latter language, you'd write the
equivalent of 'A = A = A' in C, since the middle term is both on the
left of '=', and on the right!)
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.
At the lower level it might be push/pop, load/store, or even *&A = *&A,
but in all cases you typically use the same levels of indirection on
both sides.
[...]
In more complicated cases in languages, then some asymmetry does come
up. For example, suppose C allowed this (my language allows the
equivalent):
(c ? a : b) = x;
So this assigns to either a or b depending on c. My implementation effectively turns it into this:
*(c ? &a : &b) = x;
So using explicit references and derefs. However, that is internal. The symmetry still exists in the syntax:
(c ? a : b) = (c ? a : b);
On 07.09.2024 12:53, Bart wrote:
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.
At the lower level it might be push/pop, load/store, or even *&A = *&A,
but in all cases you typically use the same levels of indirection on
both sides.
No. (And I think here lies your misconception or irritations concerning
the term "symmetry".) When writing 'A = A' there is still a semantical _asymmetry_ in "C", it only appears symmetric (because of the chosen
operator symbol and the implicit 'deref' operation which is typical in
most programming languages).
Janis
[...]
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Michael S <already5chosen@yahoo.com> writes:
On Fri, 6 Sep 2024 10:35:16 +0100
Bart <bc@freeuk.com> wrote:
On 05/09/2024 22:37, James Kuyper wrote:
On 9/5/24 12:54, Kaz Kylheku wrote:
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:
...
You seem to miss the point that assigment operator is
fundamentally assymetic.
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
That means that for you, there is no interesting difference (using
my example of assigning A to itself) in a language where you write
'A = A', and one where you write 'A = .A'.
(I'd be interested in how, in the latter language, you'd write the
equivalent of 'A = A = A' in C, since the middle term is both on
the left of '=', and on the right!)
The point is that in BLISS everithing that is legal on the right
side of asignment is also legal on the left side.
I don't know if the point is generally true. In particular, if
BLISS supports floatig point, what is meaning of floating point on
the left side?
BLISS is word based and typeless. On a PDP-10, doing a
.pi = 0
where 'pi' holds a 36-bit floating-point value (and 3.14159...
presumably), that floating-point value would be used as an
address and 0 would be stored into it (assuming I remember
BLISS correctly).
So probably not what one wants to do. ;)
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does
come up. For example, suppose C allowed this (my language allows the equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
On Sun, 8 Sep 2024 05:44:16 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does
come up. For example, suppose C allowed this (my language allows the
equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
Are you sure?
It seems to me that you got it backward.
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does come
up. For example, suppose C allowed this (my language allows the
equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
So this assigns to either a or b depending on c. My implementation
effectively turns it into this:
*(c ? &a : &b) = x;
So using explicit references and derefs. However, that is internal. The
symmetry still exists in the syntax:
(c ? a : b) = (c ? a : b);
This is only a "visual" symmetry, not a semantical one.
The LHS of the Algol 68 example is of 'REF' (lvalue) type, as it would
be the case with a language that supports a syntax as you show it here.
I'm not sure if you should adjust your wording (concerning "symmetry")
given that you seem to widely inflict confusion here.
Janis
Bart <bc@freeuk.com> wrote:
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.
: 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.
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
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.
I am not going to write substantial programs in Bliss or Forth
but I have no double they are HLL-s.
That's why I immediately see the necessity that compiler creators need
to know them in detail to _implement_ "C". And that's why I cannot see
how the statement of the C-standard's "most important purpose" would> sound reasonable (to me). ...
... I mean, what will a programmer get from the
"C" standard that a well written text book doesn't provide?
On 08/09/2024 09:58, Michael S wrote:
On Sun, 8 Sep 2024 05:44:16 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does
come up. For example, suppose C allowed this (my language allows
the equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
Are you sure?
It seems to me that you got it backward.
The point here is that you can write such a 2-way select on the LHS
of an assignment. C doesn't allow that unless you wrap it up as a
pointer expression:
*(c ? &a : &b) = x;
In language like C, the LHS of an assignment is one of four
categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
A is a simple variable; X represents a term of any complexity, and Y
is any expression. (In C, the middle two are really the same thing.)
Some languages allow extra things on the LHS, but in C they can be
emulated by transforming the term to a pointer operation. In the same
it can emulate pass-by-reference (which objects which are not arrays!)
On Sun, 8 Sep 2024 05:44:16 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does
come up. For example, suppose C allowed this (my language allows the
equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
Are you sure?
It seems to me that you got it backward.
On 9/8/24 00:39, Janis Papanagnou wrote:
...
That's why I immediately see the necessity that compiler creators needsound reasonable (to me). ...
to know them in detail to _implement_ "C". And that's why I cannot see
how the statement of the C-standard's "most important purpose" would
I agree - the most important purpose is for implementors, not developers.
... I mean, what will a programmer get from the
"C" standard that a well written text book doesn't provide?
What the C standard says is more precise and more complete than what
most textbooks say.
Most important for my purposes, it makes it clear
what's required and allowed by the standard.
For most of my career, I
worked under rules that required my code to avoid undefined behavior, to
work correctly regardless of which choice implementations make on
unspecified behavior, with a few exceptions.
In language like C, the LHS of an assignment is one of four categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
A is a simple variable;
X represents a term of any complexity, and Y is any
expression.
(In C, the middle two are really the same thing.)
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);
}
On 08/09/2024 04:44, Janis Papanagnou wrote:
On 06.09.2024 13:34, Bart wrote:
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
But the feature (using them in lvalue contexts) was rarely used.
[...]
This is only a "visual" symmetry, not a semantical one.
The LHS of the Algol 68 example is of 'REF' (lvalue) type, as it would
be the case with a language that supports a syntax as you show it here.
This is where I differ from Algol68,
where I had to considerably
simplify the semantics to get something I could understand and implement.
Take this C:
int A, B;
A = B;
There are two types associated with the LHS: 'int*' which is the type
the name A (its address), and 'int' which is the type of A's value.
So, why would a language choose int* over int as /the/ type of an
assignment target?
As far as the user is concerned, they're only dealing
with int value types.
If they have to consider that variables have addresses, then the same
applies to B!
This is where I think Algol68 got it badly wrong.
Bart <bc@freeuk.com> writes:
In language like C, the LHS of an assignment is one of four categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
A is a simple variable;
C does not define the term "simple variable" so presumably you define it
to be any named object that /can/ appear on the LHS of a simple
assignment -- a sort of "no true Scots-variable".
X represents a term of any complexity, and Y is any
expression.
I can think of at least one expression form for X that contradicts this claim.
It would be great if C had simple rules, but it doesn't. You could have started by saying something about the most comment forms of assignment
being those you list, and that X can be almost any term, but the risk of making absolute claims is that people (like me) will look into them.
Bart <bc@freeuk.com> writes:
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:
Is there no part of C you can't misrepresent?
#include <stdio.h>
void assign(int* lhs, int* rhs) {
*lhs = *rhs;
}
int main(void) {
int a=20;
assign(&a, &(int){a+1});
This is simply an anonymous object. You could have used a named object
and it wold not have been any further from being a "reference to a+1".
On 08.09.2024 10:58, Michael S wrote:
On Sun, 8 Sep 2024 05:44:16 +0200
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 06.09.2024 13:34, Bart wrote:
In more complicated cases in languages, then some asymmetry does
come up. For example, suppose C allowed this (my language allows
the equivalent):
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
Are you sure?
Sure about what? - That the code above works? - Yes, it does.
It seems to me that you got it backward.
Mind to elaborate?
Janis
On 08.09.2024 12:18, Bart wrote:
On 08/09/2024 04:44, Janis Papanagnou wrote:
On 06.09.2024 13:34, Bart wrote:
(c ? a : b) = x;
In Algol 68 you can write
IF c THEN a ELSE b FI := x
or, in a shorter form, as
( c | a | b ) := x
if you prefer.
But the feature (using them in lvalue contexts) was rarely used.
Sure.
[...]
This is only a "visual" symmetry, not a semantical one.
The LHS of the Algol 68 example is of 'REF' (lvalue) type, as it would
be the case with a language that supports a syntax as you show it here.
This is where I differ from Algol68,
Since Algol 68 is conceptually an extremely well designed language
I don't expect such formally elaborated and consistent design in
any language of much lower level.
where I had to considerably
simplify the semantics to get something I could understand and implement.
Take this C:
int A, B;
A = B;
There are two types associated with the LHS: 'int*' which is the type
the name A (its address), and 'int' which is the type of A's value.
Erm, no. The LHS of the assignment is a 'ref' 'int'; in "C" and in
(almost) all other languages I encountered.
- If you have an issue
in seeing that, and with your decades of engagement with computers,
you may now have a serious practical effort to fix that view.
This is where I think Algol68 got it badly wrong.
I strongly suspect you have no clue.
Algol 68 as probably the formally mostly elaborated and consistent
language defines the assignment semantics not differently from any
other of the many existing languages that work with variables.
On 08/09/2024 01:05, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
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.
You're doing a fine job of it!
For me it's one of those languages, like Brainf*ck, which is trivial to implement (I've done both), but next to impossible to code in.
With Forth, I had to look for sample programs to try out, and discovered
that Forth was really a myriad different dialects. It's more of a DIY language that you make up as you go along.
: 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.
This sounds like one of my bytecode languages.
So, in the same way that Lisp looks like the output of an AST dump,
these stack languages look like intermediate code:
HLL -> AST -> Stack IL -> Interpret or -> ASM
(eg. C) (Lisp) (Forth)
(Pop-11)
Most people prefer to code in a HLL.
But this at least shows Lisp as
being higher level than Forth, and it's a language that can also be bootstrapped from a tiny implementation.
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.
Around that time I was working on several languages that were low level
and with small implementations, which included running directly on 8-bit hardware. They all looked like proper HLLS, if crude and simple.
There was no need to go 'weird'. For lower level, I used assembly, where
you weren't constrained to a stack.
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.
My exposure before I first looked at C was to Algol, Pascal, Fortran
(and COBOL).
C struck me as crude, and I would have placed it lower than FORTRAN IV,
even though the latter had no structured statements. But that was
because it exposed less in the language - you couldn't play around with variable addresses for example. So FORTRAN was no good for systems programming.
As a devils...
advocate let me compare typical implementation of early Pascal
of early languages were at lower level than Pascal.
You're taking all those, to me, chaotic features of C as being superior
to Pascal.
Like being able define anonymous structs always anywhere, or allowing multiple declarations of the same module-level variables and functions.
Pascal was a teaching language and some thought went into its structure (unlike C).
In my hands I would have given it some tweaks to make it a
viable systems language. For a better evolution of Pascal, forget Wirth,
look at Ada, even thought that is not my thing because it is too strict
for my style.
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.
Syntax IS superficial! But it's pretty important otherwise we'd be programming in binary machine code, or lambda calculus.
I am not going to write substantial programs in Bliss or Forth
but I have no double they are HLL-s.
So, what would a non-HLL look like to you that is not actual assembly?
But the feature (using them in lvalue contexts) was rarely used.
(** This is:
(a, b, c) = 0;
in C, which is illegal. But put in explicit pointers, and it's suddenly
fine:
*(a, b, &c) = 0;
So why can't the compiler do that?)
Bart <bc@freeuk.com> wrote:
Like being able define anonymous structs always anywhere, or allowing
multiple declarations of the same module-level variables and functions.
Look at this C code:
void
do_bgi_add(unsigned int * dst, int xlen, unsigned int * xp,
int ylen, unsigned int * yp) {
if (ylen < xlen) {
int tmp = xlen;
xlen = ylen;
ylen = tmp;
unsigned int * tmpp = xp;
xp = yp;
yp = tmpp;
}
unsigned int xext = (unsigned int)(((int)(xp[xlen - 1])) >> 31);
unsigned int yext = (unsigned int)(((int)(yp[ylen - 1])) >> 31);
unsigned int c = 0;
int i = 0;
while(i < xlen) {
unsigned long long pp = (unsigned long long)(xp[i])
+ (unsigned long long)(yp[i])
+ (unsigned long long)c;
dst[i] = pp;
c = (pp >> (32ULL));
i++;
}
while(i < ylen) {
unsigned long long pp = (unsigned long long)xext
+ (unsigned long long)(yp[i])
+ (unsigned long long)c;
dst[i] = pp;
c = (pp >> (32ULL));
i++;
}
{
unsigned long long pp = (unsigned long long)xext
+ (unsigned long long)yext
+ (unsigned long long)c;
dst[i] = pp;
}
}
I claim that is is better than what could be done in early Pascal.
Temporary variables are declared exactly in scopes where they are
needed, I reuse the same name for 'pp' but scoping makes clear
that different 'pp' are different variables. All variables
are initialised at declaration time with sensible values. Only
parameters, 'i' and 'c' are common to various stages, they have
to. Note that 'xext' and 'yext' are declared at point where I
can compute initial value. Also note that among ordinary
variables only 'i' and 'c' are reassigned (I need to swap parameters
to simplify logic and 'dst' array entries are assigned as part of
function contract). Fact that variables are not reassigned could
be made clearer by declaring them as 'const'.
Bart <bc@freeuk.com> wrote:
On 08/09/2024 01:05, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
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.
You're doing a fine job of it!
For me it's one of those languages, like Brainf*ck, which is trivial to
implement (I've done both), but next to impossible to code in.
I wonder if you really implemented Forth. Did you implement immediate
words? POSTPONE?
define fibr(n);
if n < 2 then 1 else fibr(n - 1) + fibr(n - 2) endif
enddefine;
There are some anomalies, assignment is written as:
a -> b;
which means assign a to b.
external declare tse in oldc;
void
tse1(da, ia, sa, dp, ip, sp, d, i, s)
double da[];
int ia[];
float sa[];
double * dp;
int * ip;
float * sp;
double d;
int i;
float s;
{
}
endexternal;
The first line switches on syntax extention and lines after that
up to 'endexternal' are parsed as C code. More procisely, 'oldc'
expects KR style function definitions (with empty body). As
result this piece of code generates Pop11 wrapper that call
corresponding C routine passing it arguments of types specified
in C definition (but on Pop11 side 'double da[]' and 'double * dp'
are treated differently). Note that this in _not_ a feature of
core Pop11 langrage. Rather it is handled by library code (which
in principle could by written by ordinary user.
Most people prefer to code in a HLL.
You mean "most people prefer by now traditional syntax", sure.
But this at least shows Lisp as
being higher level than Forth, and it's a language that can also be
bootstrapped from a tiny implementation.
Pop11 has very similar semantics to Lisp and resonably traditional
syntax. I would say that Lisp users value more extensibility than traditional syntax. Namely Lisp macros give powerful extensibility
and they work naturaly with prefix syntax. In particular, to do
an extension you specify tree transformation, that is transformation
of Lisp "lists", which can be done conveniently in Lisp. In Pop11
extentions usually involve some hooks into parser and one need to
think how parsing work. And one needs to generate representation
at lower level. So extending Pop11 usualy is more work than extending
Lisp. I also use a language called Boot, you will not like it
because it uses whitespace to denote blocks. Semantics of Boot is essentially the same as Lisp and systax (expect for whitespace)
is Algol-like.
One of my early program did recursive walk on a graph. Goal was
to do some circuit computation. I did it in Basic on ZX 81.
I used GOSUB to do recursive calls, but had to simulate the argument
stack with arrays. This led to severl compilcations, purely
because of inadequacy of the language.
You're taking all those, to me, chaotic features of C as being superior
to Pascal.
Like being able define anonymous structs always anywhere, or allowing
multiple declarations of the same module-level variables and functions.
Look at this C code:
In my hands I would have given it some tweaks to make it a
viable systems language. For a better evolution of Pascal, forget Wirth,
look at Ada, even thought that is not my thing because it is too strict
for my style.
For system programming Turbo Pascal had what is needed.
Syntax IS superficial! But it's pretty important otherwise we'd be
programming in binary machine code, or lambda calculus.
Well, even in context of systax dot required by Bliss is little thing,
just a bit of mandatory noise.
Concerning Forth and Lisp, some people like such syntax and there are
gains.
I am not going to write substantial programs in Bliss or Forth
but I have no double they are HLL-s.
So, what would a non-HLL look like to you that is not actual assembly?
In first approximation HLL = not(assembly). Of course (binary, octal,
etc) machine language counts as assembly for purpose of this equation.
And some language like PL360 or Randall Hyde HLA which have constructs
which does not look like assembly still count as assembly. Similarly
macro assemblers like IBM HLA count as assembly. Fact that there is
a complier that turns IBM HLA Javascript, so that you can run it on
wide range of machines does not change this. For me what count is
thinking and intent: using IBM HLA you still need to think in term
of machine instructions. One can use macro assemblers to provide
set of macros such that user of those macros does not need to think
about machine instructions. IIUC one of PL/I compilers was created
by using a macro assembler to implement a small subset of PL/I, and
then proper PL/I compiler was written in this subset. But this
is really using macro assembler to implement different language.
In other words, once extentions can function as independent language
and users are encouraged to think in terms of new language,
this is no longer assembler, but a new thing.
Just as an extra explanantion, I read HLL as Higher Level Language,
with Higher implitely referencing assembly. So it does not need
to be very high level, just higher level than assembly.
And while microcontrollers sometimes have a limited form of branch
prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution.
Bart <bc@freeuk.com> writes:
[...]
I had a problem with this code because it was so verbose. The first
thing I did was to define aliases u64 and u32 for those long types:
typedef unsigned long long u64;
typedef unsigned long u32;
So you're assuming that unsigned long is 32 bits? (It's 64 bits on the systems I use most.)
Bart <bc@freeuk.com> writes:
On 08/09/2024 22:15, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]
I had a problem with this code because it was so verbose. The firstSo you're assuming that unsigned long is 32 bits? (It's 64 bits on
thing I did was to define aliases u64 and u32 for those long types:
typedef unsigned long long u64;
typedef unsigned long u32;
the systems I use most.)
That should be unsigned int.
So you're assumuing that unsigned int is 32 bits?
I know you're aware of <stdint.h>. You can use it to define your own
u64 and 32 aliases if you like.
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit
machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
In my version type of pp is clearly visible, together with casts
this gives string hint what is happening: this is 32-bit addition
producing carry in 'c'.
Note, I did not intend to post a trap for you, but in your
egerness to shorten the code you removed important information.
And while this code is unlikely to change much (basically
upgrade to 64-bit version on 64-bit machines in the only likely
change), normally code evolves and your version is harder
to change.
More generally, my aim is to make code obviously correct
(I not saying that I was fully successful in this case).
I consider your version worse, because with your version
reader has more work checking correctness (even taking into
account that you lowered number of lines).
Anyway, I illustrated to you how I use declarations in the middle
of a function. There is nothing chaotic about this, type is
declared when variable first time gets its value. And in most
cases variable scope is tiny. AFAICS neither early Pascal nor
your language allows me to write programs in this way.
And if
you do not see benefits, well, this your loss.
On 08/09/2024 19:13, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
Like being able define anonymous structs always anywhere, or allowing
multiple declarations of the same module-level variables and functions.
Look at this C code:
void
do_bgi_add(unsigned int * dst, int xlen, unsigned int * xp,
int ylen, unsigned int * yp) {
if (ylen < xlen) {
int tmp = xlen;
xlen = ylen;
ylen = tmp;
unsigned int * tmpp = xp;
xp = yp;
yp = tmpp;
}
unsigned int xext = (unsigned int)(((int)(xp[xlen - 1])) >> 31);
unsigned int yext = (unsigned int)(((int)(yp[ylen - 1])) >> 31);
unsigned int c = 0;
int i = 0;
while(i < xlen) {
unsigned long long pp = (unsigned long long)(xp[i])
+ (unsigned long long)(yp[i])
+ (unsigned long long)c;
dst[i] = pp;
c = (pp >> (32ULL));
i++;
}
while(i < ylen) {
unsigned long long pp = (unsigned long long)xext
+ (unsigned long long)(yp[i])
+ (unsigned long long)c;
dst[i] = pp;
c = (pp >> (32ULL));
i++;
}
{
unsigned long long pp = (unsigned long long)xext
+ (unsigned long long)yext
+ (unsigned long long)c;
dst[i] = pp;
}
}
I claim that is is better than what could be done in early Pascal.
Temporary variables are declared exactly in scopes where they are
needed, I reuse the same name for 'pp' but scoping makes clear
that different 'pp' are different variables. All variables
are initialised at declaration time with sensible values. Only
parameters, 'i' and 'c' are common to various stages, they have
to. Note that 'xext' and 'yext' are declared at point where I
can compute initial value. Also note that among ordinary
variables only 'i' and 'c' are reassigned (I need to swap parameters
to simplify logic and 'dst' array entries are assigned as part of
function contract). Fact that variables are not reassigned could
be made clearer by declaring them as 'const'.
I had a problem with this code because it was so verbose. The first
thing I did was to define aliases u64 and u32 for those long types:
typedef unsigned long long u64;
typedef unsigned long u32;
Then I removed some casts that I thought were not necessary.
The first
result looks like this:
---------------------------
void do_bgi_add(u32 * dst, int xlen, u32 * xp, int ylen, u32 * yp) {
u32 xext, yext, c;
u64 pp;
int i;
if (ylen < xlen) {
int tmp = xlen;
xlen = ylen;
ylen = tmp;
u32 * tmpp = xp;
xp = yp;
yp = tmpp;
}
xext = ((int)(xp[xlen - 1])) >> 31;
yext = ((int)(yp[ylen - 1])) >> 31;
c = 0;
i = 0;
while(i < xlen) {
pp = (u64)(xp[i]) + (u64)yp[i] + c;
dst[i] = pp;
c = pp >> 32;
i++;
}
while(i < ylen) {
pp = (u64)xext + (u64)yp[i] + c;
dst[i] = pp;
c = pp >> 32;
i++;
}
pp = (u64)xext + (u64)yext + c;
dst[i] = pp;
}
---------------------------
Things actually fit onto one line! It's easier now to grasp what's going
on. There are still quite a few casts; it would be better if xext/yext/c
were all u64 type instead of u32.
pp seems to used for the same purpose throughout, so I can't see the
point in declaring three separate versions of the same thing.
On 08/09/2024 19:13, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 08/09/2024 01:05, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
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.
You're doing a fine job of it!
For me it's one of those languages, like Brainf*ck, which is trivial to
implement (I've done both), but next to impossible to code in.
I wonder if you really implemented Forth. Did you implement immediate
words? POSTPONE?
I implemented a toy version, with 35 predefined words, that was enough
to implement Fizz Buzz. Then I looked for more examples to try and found
they all assumed slightly different sets of built-ins.
define fibr(n);
if n < 2 then 1 else fibr(n - 1) + fibr(n - 2) endif
enddefine;
There are some anomalies, assignment is written as:
a -> b;
which means assign a to b.
This POP11 seems a more viable language than Forth. (I vaguely remember something from college days, but that might have been POP2.)
external declare tse in oldc;
void
tse1(da, ia, sa, dp, ip, sp, d, i, s)
double da[];
int ia[];
float sa[];
double * dp;
int * ip;
float * sp;
double d;
int i;
float s;
{
}
endexternal;
The first line switches on syntax extention and lines after that
up to 'endexternal' are parsed as C code. More procisely, 'oldc'
expects KR style function definitions (with empty body). As
result this piece of code generates Pop11 wrapper that call
corresponding C routine passing it arguments of types specified
in C definition (but on Pop11 side 'double da[]' and 'double * dp'
are treated differently). Note that this in _not_ a feature of
core Pop11 langrage. Rather it is handled by library code (which
in principle could by written by ordinary user.
I don't quite understand that. Who or what is the C syntax for?
Does POP11 transpile to C?
Usually the FFI of a language uses bindings expressed in that language,
and is needed for the implementation to generate the correct code. So
it's not clear if calls to that function are checked for numbers and
types of arguments.
Pop11 has very similar semantics to Lisp and resonably traditional
syntax. I would say that Lisp users value more extensibility than
traditional syntax. Namely Lisp macros give powerful extensibility
and they work naturaly with prefix syntax. In particular, to do
an extension you specify tree transformation, that is transformation
of Lisp "lists", which can be done conveniently in Lisp. In Pop11
extentions usually involve some hooks into parser and one need to
think how parsing work. And one needs to generate representation
at lower level. So extending Pop11 usualy is more work than extending
Lisp. I also use a language called Boot, you will not like it
because it uses whitespace to denote blocks. Semantics of Boot is
essentially the same as Lisp and systax (expect for whitespace)
is Algol-like.
One of yours? A search for Boot PL didn't show anything relevant.
One of my early program did recursive walk on a graph. Goal was
to do some circuit computation. I did it in Basic on ZX 81.
I used GOSUB to do recursive calls, but had to simulate the argument
stack with arrays. This led to severl compilcations, purely
because of inadequacy of the language.
On ZX81? I can imagine it being hard! (Someone wanted me to do something
on ZX80, but I turned it down. I considered it too much of a toy.)
Waldek Hebisch <antispam@fricas.org> writes:
Bart <bc@freeuk.com> wrote:[...]
I had a problem with this code because it was so verbose. The first
thing I did was to define aliases u64 and u32 for those long types:
typedef unsigned long long u64;
typedef unsigned long u32;
This code runs in 33 bit i386, 32 bit ARM and 64 bit x86-64, in
all cases under Linux. As Keith noticed, most popular of those
has 64-bit long. So your definition would break it. You need
typedef unsigned int u32;
Just add #include <stdint.h> and use uint32_t -- or, if you value
brevity for some reason:
typedef uint32_t u32;
(Bart dislikes <stdint.h>, but there's no reason you should.)
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit
machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless
intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables
were used in loops where they need to be widened to 64 bits anyway. The
new value of c is set from a 32-bit result.
(Have you tried 64-bit versions of xext, yext, c to see it it makes any difference? I may try it myself if I can set up a suitable test, but I
can only test on a 64-bit machine.
Do you still have 32-bit machines around? I haven't been able to find
one for a decade and a half!)
And if
you do not see benefits, well, this your loss.
Average number of local variables in a half-dozen C codebases I surveyed
was 3 variables per function. So I find it hard to see the point of
splitting them up into different scopes!
This code runs in 33 bit i386, ...
Well, I had 48KB ZX Spectrum. On it I could run Hisoft Pascal,
Hisoft C, FIG-Forth and other things. At that time I had some
knowledge of C but really did not understand it. Hisoft C gave
me similar speed to Hisoft Pascal, had less features (C lacked
floating point) and needed more memory. FIG-Forth needed very
little memory but execution speed was significantly worse than
Pascal. And my programs were small. Some my programs needed
enough momory for data, but one could write a compiled progam
to the tape and run it from the tape. So I mostly used Pascal.
But that could be different with less memory (compiler would
not run on 16kB Spectrum, FIG-Forth would be happy on it) or
with bigger programs.
On 08.09.2024 16:12, James Kuyper wrote:
On 9/8/24 00:39, Janis Papanagnou wrote:
...
That's why I immediately see the necessity that compiler creators needsound reasonable (to me). ...
to know them in detail to _implement_ "C". And that's why I cannot see
how the statement of the C-standard's "most important purpose" would
I agree - the most important purpose is for implementors, not developers.
... I mean, what will a programmer get from the
"C" standard that a well written text book doesn't provide?
What the C standard says is more precise and more complete than what
most textbooks say.
Exactly. And this precision is what makes standard often difficult
to read (for programming purposes for "ordinary" folks).
If a textbook doesn't answer a question I have I'd switch to another
(better) textbook, (usually) not to the standard.
Most important for my purposes, it makes it clear
what's required and allowed by the standard.
To be honest, I was also inspecting and reading language standards
(e.g. for the POSIX shell and awk), but not to be able to correctly
write programs in those languages, but rather out of interest and
for academical discussions in Usenet (like the discussions here,
that also often refer to the "C" standard).
For most of my career, I
worked under rules that required my code to avoid undefined behavior, to
work correctly regardless of which choice implementations make on
unspecified behavior, with a few exceptions.
Janis
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit
machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless
intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables
were used in loops where they need to be widened to 64 bits anyway. The
new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler
should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
Average number of local variables in a half-dozen C codebases I surveyed
was 3 variables per function. So I find it hard to see the point of
splitting them up into different scopes!
My style tends to produce more local variables than older style.
Some functions are big and in those there are most benefits.
But even if there is only 1 variable wrong/missing initialization
may be a problem. My style minimizes such issues.
David Brown <david.brown@hesbynett.no> wrote:
And while microcontrollers sometimes have a limited form of branch
prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution.
IIUC STM4 series has cache, and some of them are not so big. There
are now several chinese variants of STM32F103 and some of them have
caches (some very small like 32 words, IIRC one has 8 words and it
is hard to decide if this very small cache or big prefetch buffer).
A notable example is MH32F103. Base model officially has 64kB RAM and
256KB flash. AFAIK this flash is rather slow SPI flash. It also
has 16kB cache which probably is 4-way set associative with few
extra lines (probably 4) to increase apparent associativity.
I write probably because this is result of reasoning based on
several time measurements. If you hit cache it runs nicely at
216 MHz. Cache miss costs around 100 clocks (varies depending on
exact setting of timing parameters and form of access).
Similar technology seem to be popular among chines chip makers,
especially for "bigger" chips. But IIUC GD use is for chips
of size of STM32F103C8T6.
On 08/09/2024 01:05, Waldek Hebisch wrote:
First, it is not my goal to advocate for Forth use.
For me it's one of those languages, like Brainf*ck, which is trivial to implement (I've done both), but next to impossible to code in.
[...]
Pascal was a teaching language and some thought went into its structure (unlike C). In my hands I would have given it some tweaks to make it a
viable systems language. For a better evolution of Pascal, forget Wirth,
look at Ada, even thought that is not my thing because it is too strict
for my style.
Bart <bc@freeuk.com> wrote:
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.
[...] As a devils
advocate let me compare typical implementation of early Pascal
with C modern C. [...] 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.
[...] 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.
On 08/09/2024 16:37, Janis Papanagnou wrote:...
On 08.09.2024 16:12, James Kuyper wrote:
Most important for my purposes, it makes it clear
what's required and allowed by the standard.
No, that is not really true - the C standard is /not/ clear on all
points. There are aspects of the language that you cannot fully
understand without cross-referencing between many different sections
(and there are a few aspects that are not clear even then). That is
because it is a standard, not a tutorial, and not a language reference.
A standard is written in more "legalise" language, and makes a point of trying to avoid repeating itself - while a good reference will repeat
the same information multiple times in different places, whenever it
helps for clarity.
On 08/09/2024 19:13, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
Like being able define anonymous structs always anywhere, or allowing
multiple declarations of the same module-level variables and functions.
Look at this C code:
[ snip code ]
I claim that is is better than what could be done in early Pascal.
Temporary variables are declared exactly in scopes where they are
needed, I reuse the same name for 'pp' but scoping makes clear
that different 'pp' are different variables. All variables
are initialised at declaration time with sensible values. Only
parameters, 'i' and 'c' are common to various stages, they have
to. Note that 'xext' and 'yext' are declared at point where I
can compute initial value. Also note that among ordinary
variables only 'i' and 'c' are reassigned (I need to swap parameters
to simplify logic and 'dst' array entries are assigned as part of
function contract). Fact that variables are not reassigned could
be made clearer by declaring them as 'const'.
I had a problem with this code because it was so verbose. [...]
[ snip try of a refactoring ]
On 9/9/24 04:46, David Brown wrote:
On 08/09/2024 16:37, Janis Papanagnou wrote:...
On 08.09.2024 16:12, James Kuyper wrote:
Most important for my purposes, it makes it clear
what's required and allowed by the standard.
No, that is not really true - the C standard is /not/ clear on all
points. There are aspects of the language that you cannot fully
understand without cross-referencing between many different sections
(and there are a few aspects that are not clear even then). That is
because it is a standard, not a tutorial, and not a language reference.
A standard is written in more "legalise" language, and makes a point of
trying to avoid repeating itself - while a good reference will repeat
the same information multiple times in different places, whenever it
helps for clarity.
I will concede your point, but it's still the case that the standard is clearer about such things than any other source I'm familiar with.
Bart <bc@freeuk.com> wrote:
On 08/09/2024 19:13, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 08/09/2024 01:05, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
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.
You're doing a fine job of it!
For me it's one of those languages, like Brainf*ck, which is trivial to >>>> implement (I've done both), but next to impossible to code in.
I wonder if you really implemented Forth. Did you implement immediate
words? POSTPONE?
I implemented a toy version, with 35 predefined words, that was enough
to implement Fizz Buzz. Then I looked for more examples to try and found
they all assumed slightly different sets of built-ins.
OK, so apparently you missed essential part.
On ZX81? I can imagine it being hard! (Someone wanted me to do something
on ZX80, but I turned it down. I considered it too much of a toy.)
To give more background, bare ZX81 had 1kB RAM (including video RAM).
On 09/09/2024 13:03, James Kuyper wrote:
On 9/9/24 04:46, David Brown wrote:
On 08/09/2024 16:37, Janis Papanagnou wrote:...
On 08.09.2024 16:12, James Kuyper wrote:
Most important for my purposes, it makes it clear
what's required and allowed by the standard.
No, that is not really true - the C standard is /not/ clear on all
points. There are aspects of the language that you cannot fully
understand without cross-referencing between many different sections
(and there are a few aspects that are not clear even then). That is
because it is a standard, not a tutorial, and not a language reference.
A standard is written in more "legalise" language, and makes a point of
trying to avoid repeating itself - while a good reference will repeat
the same information multiple times in different places, whenever it
helps for clarity.
I will concede your point, but it's still the case that the standard is
clearer about such things than any other source I'm familiar with.
I have lost track of which particular "such things" we are talking about here, so you could well be right!
The standard /is/ clear on some aspects of C - but not on others. I
don't dispute that it is a useful document and one that serious C
programmers should aspire to read, but I don't think it is really aimed
at "normal" C programmers or useful to them. Perhaps the original
writers did not envisage so many non-experts getting involved in C coding.
Waldek Hebisch <antispam@fricas.org> writes:...
1. Well, I used to care about pre-Ansi compilers and for benefit
to them I got into habit of avoiding <stdint.h>. Yes, it is time
to change and on microcontrollers I use <stdint.h> as sizes
are important there and I do not expect to ever compile
microcontroller code with non-Ansi compiler.
Pre-ANSI? I haven't been even been able to find a working pre-ANSI
C compiler.
On 08/09/2024 23:34, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
And while microcontrollers sometimes have a limited form of branch
prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution.
IIUC STM4 series has cache, and some of them are not so big. There
are now several chinese variants of STM32F103 and some of them have
caches (some very small like 32 words, IIRC one has 8 words and it
is hard to decide if this very small cache or big prefetch buffer).
There are different kinds of cache here. Some of the Cortex-M cores
have optional caches (i.e., the microcontroller manufacturer can choose
to have them or not).
<https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization>
Flash memory, flash controller peripherals, external memory interfaces (including things like QSPI) are all specific to the manufacturer,
rather than part of the Cortex M cores from ARM. Manufacturers can do whatever they want there.
So a "cache" of 32 words is going to be part of the flash interface, not
a cpu cache
(which are typically 16KB - 64KB,
and only found on bigger
microcontrollers with speeds of perhaps 120 MHz or above). And yes, it
is often fair to call these flash caches "prefetch buffers" or
read-ahead buffers.
(You also sometimes see small caches for external
ram or dram interfaces.)
A notable example is MH32F103. Base model officially has 64kB RAM and
256KB flash. AFAIK this flash is rather slow SPI flash. It also
has 16kB cache which probably is 4-way set associative with few
extra lines (probably 4) to increase apparent associativity.
I write probably because this is result of reasoning based on
several time measurements. If you hit cache it runs nicely at
216 MHz. Cache miss costs around 100 clocks (varies depending on
exact setting of timing parameters and form of access).
Similar technology seem to be popular among chines chip makers,
especially for "bigger" chips. But IIUC GD use is for chips
of size of STM32F103C8T6.
David Brown <david.brown@hesbynett.no> wrote:
On 08/09/2024 23:34, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
IIUC STM4 series has cache, and some of them are not so big. There
And while microcontrollers sometimes have a limited form of branch
prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution. >>>
are now several chinese variants of STM32F103 and some of them have
caches (some very small like 32 words, IIRC one has 8 words and it
is hard to decide if this very small cache or big prefetch buffer).
There are different kinds of cache here. Some of the Cortex-M cores
have optional caches (i.e., the microcontroller manufacturer can choose
to have them or not).
<https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization>
I do not see relevent information at that link.
Flash memory, flash controller peripherals, external memory interfaces
(including things like QSPI) are all specific to the manufacturer,
rather than part of the Cortex M cores from ARM. Manufacturers can do
whatever they want there.
AFAIK typical Cortex-M design has core connected to "bus matrix".
It is up to chip vendor to decide what else is connected to bus matrix.
For me it does not matter if it is ARM design or vendor specific.
Normal internal RAM is accessed via bus matrix, and in MCU-s that
I know about is fast enough so that cache is not needed. So caches
come into play only for flash (and possibly external memory, but
design with external memory probably will be rather large).
It seems that vendor do not like to say that they use cache, instead
that use misleading terms like "flash accelerator".
So a "cache" of 32 words is going to be part of the flash interface, not
a cpu cache
Well, caches never were part of CPU proper, they were part of
memory interface. They could act for whole memory or only for part
that need it (like flash). So I do not understand what "not a cpu
cache" is supposed to mean. More relevant is if such thing act
as a cache, 32 word things almost surely will act as a cache,
8 word thing may be a simple FIFO buffer (or may act smarter
showing behaviour typical of caches).
(which are typically 16KB - 64KB,
I wonder where you found this figure. Such size is typical for
systems bigger than MCU-s. It could be useful for MCU-s with
flash a on separate die, but with flash on the same die as CPU
much smaller cache is adequate.
and only found on bigger
microcontrollers with speeds of perhaps 120 MHz or above). And yes, it
is often fair to call these flash caches "prefetch buffers" or
read-ahead buffers.
Typical code has enough branches that simple read-ahead beyond 8
words is unlikely to give good results. OTOH delivering things
that were accessed in the past and still present in the cache
gives good results even with very small caches.
(You also sometimes see small caches for external
ram or dram interfaces.)
A notable example is MH32F103. Base model officially has 64kB RAM and
256KB flash. AFAIK this flash is rather slow SPI flash. It also
has 16kB cache which probably is 4-way set associative with few
extra lines (probably 4) to increase apparent associativity.
I write probably because this is result of reasoning based on
several time measurements. If you hit cache it runs nicely at
216 MHz. Cache miss costs around 100 clocks (varies depending on
exact setting of timing parameters and form of access).
Similar technology seem to be popular among chines chip makers,
especially for "bigger" chips. But IIUC GD use is for chips
of size of STM32F103C8T6.
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit
machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless
intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables
were used in loops where they need to be widened to 64 bits anyway. The
new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler
should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the
range you need), and want it to be as efficient as possible on 32-bit
and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on 32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can
make code significantly more efficient on 64-bit systems while retaining efficiency on 32-bit (or even smaller) targets.
On 9/8/24 20:29, Waldek Hebisch wrote:
...
This code runs in 33 bit i386, ...
I'm either amazed or amused, depending upon whether or not 33 is a typo.
Bart <bc@freeuk.com> writes:
In language like C, the LHS of an assignment is one of four categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
A is a simple variable;
C does not define the term "simple variable" so presumably you
define it to be any named object that /can/ appear on the LHS
of a simple assignment -- a sort of "no true Scots-variable".
X represents a term of any complexity, and Y is any
expression.
I can think of at least one expression form for X that contradicts
this claim.
It would be great if C had simple rules, but it doesn't. You could have started by saying something about the most comment forms of assignment
being those you list, and that X can be almost any term, but the risk of making absolute claims is that people (like me) will look into them.
Waldek Hebisch <antispam@fricas.org> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Waldek Hebisch <antispam@fricas.org> writes:
Bart <bc@freeuk.com> wrote:[...]
I had a problem with this code because it was so verbose. The first
thing I did was to define aliases u64 and u32 for those long types:
typedef unsigned long long u64;
typedef unsigned long u32;
This code runs in 33 bit i386, 32 bit ARM and 64 bit x86-64, in
all cases under Linux. As Keith noticed, most popular of those
has 64-bit long. So your definition would break it. You need
typedef unsigned int u32;
Just add #include <stdint.h> and use uint32_t -- or, if you value
brevity for some reason:
typedef uint32_t u32;
(Bart dislikes <stdint.h>, but there's no reason you should.)
1. Well, I used to care about pre-Ansi compilers and for benefit
to them I got into habit of avoiding <stdint.h>. Yes, it is time
to change and on microcontrollers I use <stdint.h> as sizes
are important there and I do not expect to ever compile
microcontroller code with non-Ansi compiler.
Pre-ANSI? I haven't been even been able to find a working pre-ANSI
C compiler.
<stdint.h> was added in C99, so a C89/C90 ("ANSI") implementation might
not support it, but I'd still be surprised if you could find a C implementatation these days that doesn't have <stdint.h>. And you can
roll your own or use something like <https://www.lysator.liu.se/c/q8/index.html>.
2. As I explanined fixed size is just current state of developement.
On 64-bit machines code should use bigger types. And to get
128-bit type it seems that I need nonstandard type (there seem
to be new types in C23, but I did not investigate them deeper).
C23 doesn't add any new support for 128-bit integers.
gcc (and compilers intended to be compatible with it, like clang)
support __int128 and unsigned __int128 types, but only on 64-bit
systems, and they're not strictly integer types (for example, there are
no constants of type __int128).
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
gcc (and compilers intended to be compatible with it, like clang)
support __int128 and unsigned __int128 types, but only on 64-bit
systems, and they're not strictly integer types (for example, there are
no constants of type __int128).
Yes, I know that. IIUC clang supports bigger types, and in one
of drafts I found "bit exact integer types". At first glance it
looked that they could support bigger lengths than standard integer
types.
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
In language like C, the LHS of an assignment is one of four categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
Yes, very good. I count four or five, depending on what
differences count as different.
On 09/09/2024 17:21, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
So what does _Bitint do with a width of 128 bits?
On 08.09.2024 16:12, James Kuyper wrote:
On 9/8/24 00:39, Janis Papanagnou wrote:
...
That's why I immediately see the necessity that compiler creators needsound reasonable (to me). ...
to know them in detail to _implement_ "C". And that's why I cannot see
how the statement of the C-standard's "most important purpose" would
I agree - the most important purpose is for implementors, not developers.
... I mean, what will a programmer get from the
"C" standard that a well written text book doesn't provide?
What the C standard says is more precise and more complete than what
most textbooks say.
Exactly. And this precision is what makes standard often difficult
to read (for programming purposes for "ordinary" folks).
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit >>>>> machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless
intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables >>>> were used in loops where they need to be widened to 64 bits anyway. The >>>> new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler
should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the
range you need), and want it to be as efficient as possible on 32-bit
and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on
32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can
make code significantly more efficient on 64-bit systems while retaining
efficiency on 32-bit (or even smaller) targets.
Well, I have constraints, some of which are outside of C code.
To resolve contraints I normally use some configure machinery.
If a standard type is exact match for my constraints, then I would
use standard type, possibly adding some fallbacks for pre-standard
systems. But if my constranits differ, I see no advantage in
using more "fancy" standard types compared to old types.
Concerning '<stdint.h>', somewhat worring thing is that several types
there seem to be optional (or maybe where optional in older versions
of the standard). I am not sure if this can be real problem,
but too many times I saw that on various issues developers say
"this in not mandatory, so we will skip it".
On 09/09/2024 18:57, Bart wrote:
On 09/09/2024 17:21, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
So what does _Bitint do with a width of 128 bits?
_BitInt types are not "integer types". Nor is gcc's __int128 type.
The standard for a language which has operators with precedence
should give a precedence table.
On 2024-09-09, David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:57, Bart wrote:
On 09/09/2024 17:21, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
So what does _Bitint do with a width of 128 bits?
_BitInt types are not "integer types". Nor is gcc's __int128 type.
How can we write a program which, in an implementation which has a
__int128 type, outputs "yes" if it is an integer type, otherwise "no"?
On Fri, 06 Sep 2024 07:56:56 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Fri, 6 Sep 2024 10:35:16 +0100
Bart <bc@freeuk.com> wrote:
On 05/09/2024 22:37, James Kuyper wrote:
On 9/5/24 12:54, Kaz Kylheku wrote:
On 2024-09-05, Waldek Hebisch <antispam@fricas.org> wrote:
...
You seem to miss the point that assigment operator is
fundamentally assymetic.
Both sides of an assignment can be complex expressions that
designate an object (though the right side need not).
So you've correctly identified the very fundamental asymmetry.
Sure, if you want to completely disregard all the cases where the
symmetry does exist.
That means that for you, there is no interesting difference (using
my example of assigning A to itself) in a language where you write
'A = A', and one where you write 'A = .A'.
(I'd be interested in how, in the latter language, you'd write the
equivalent of 'A = A = A' in C, since the middle term is both on
the left of '=', and on the right!)
The point is that in BLISS everithing that is legal on the right
side of asignment is also legal on the left side.
I don't know if the point is generally true. In particular, if
BLISS supports floatig point, what is meaning of floating point on
the left side?
BLISS is word based and typeless. On a PDP-10, doing a
.pi = 0
where 'pi' holds a 36-bit floating-point value (and 3.14159...
presumably), that floating-point value would be used as an
address and 0 would be stored into it (assuming I remember
BLISS correctly).
On PDP-10 reinterpreting [18 LS bits of] floating-point as address is natural, because addresses, integers and FP share the same register
file.
It seems to me that on S/360 or CDC-6K or PDP-11 or VAX it would be
less natural.
However, natural or not, BLISS was used widely both on PDP-11 and on
VAX, which means that it worked well enough.
So probably not what one wants to do. ;)
Yes, LS bits of FP as address do not sound very useful.
On the other hand, using several MS bits of FP, although typically
fewer than 18, as address is useful in calculations of many
transcendental functions.
Concerning '<stdint.h>', somewhat worring thing is that several types
there seem to be optional (or maybe where optional in older versions
of the standard). I am not sure if this can be real problem,
but too many times I saw that on various issues developers say
"this in not mandatory, so we will skip it".
On 09/09/2024 18:46, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit >>>>>> machines doing otherwise would add useless instructions to object
code. More precisly, really stupid compiler will generate useless >>>>>> intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables >>>>> were used in loops where they need to be widened to 64 bits anyway. The >>>>> new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler
should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the
range you need), and want it to be as efficient as possible on 32-bit
and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on
32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can
make code significantly more efficient on 64-bit systems while retaining >>> efficiency on 32-bit (or even smaller) targets.
Well, I have constraints, some of which are outside of C code.
To resolve contraints I normally use some configure machinery.
If a standard type is exact match for my constraints, then I would
use standard type, possibly adding some fallbacks for pre-standard
systems. But if my constranits differ, I see no advantage in
using more "fancy" standard types compared to old types.
The "fancy" standard types in this case specify /exactly/ what you
apparently want - a type that can deal with at least 32 bits for range,
and is as efficient as possible on different targets. What other
constraints do you have here that make "int_fast32_t" unsuitable?
Concerning '<stdint.h>', somewhat worring thing is that several types
there seem to be optional (or maybe where optional in older versions
of the standard). I am not sure if this can be real problem,
but too many times I saw that on various issues developers say
"this in not mandatory, so we will skip it".
<stdint.h> has been required since C99. It has not been optional in any
C standard in the last 25 years. That's a /long/ time - long enough for
most people to be able to say "it's standard".
And almost every C90 compiler also includes <stdint.h> as an extension.
If you manage to find a compiler that is old enough not to have
<stdint.h>, and you need to use it for actual code, it's probably worth spending the 3 minutes it takes to write a suitable <stdint.h> yourself
for that target.
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
In language like C, the LHS of an assignment is one of four categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
OK, so what are they?
Yes, very good. I count four or five, depending on whatdifferences count as different.
I can think of at least one expression form for X that contradicts this
claim.
Example?
On 09/09/2024 20:46, Kaz Kylheku wrote:
On 2024-09-09, David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:57, Bart wrote:
On 09/09/2024 17:21, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
So what does _Bitint do with a width of 128 bits?
_BitInt types are not "integer types". Nor is gcc's __int128 type.
How can we write a program which, in an implementation which has a
__int128 type, outputs "yes" if it is an integer type, otherwise "no"?
#include <stdio.h>
int main() {
auto const x = 0x1'0000'0000'0000'0000;
if (x > 0xffff'ffff'ffff'ffff) {
printf("yes\n");
} else {
printf("no\n");
}
}
On 09/09/2024 16:36, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 08/09/2024 23:34, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
IIUC STM4 series has cache, and some of them are not so big. There
And while microcontrollers sometimes have a limited form of branch
prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution. >>>>
are now several chinese variants of STM32F103 and some of them have
caches (some very small like 32 words, IIRC one has 8 words and it
is hard to decide if this very small cache or big prefetch buffer).
There are different kinds of cache here. Some of the Cortex-M cores
have optional caches (i.e., the microcontroller manufacturer can choose
to have them or not).
<https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization>
I do not see relevent information at that link.
There is a table of the Cortex-M cores, with the sizes of the optional caches.
Flash memory, flash controller peripherals, external memory interfaces
(including things like QSPI) are all specific to the manufacturer,
rather than part of the Cortex M cores from ARM. Manufacturers can do
whatever they want there.
AFAIK typical Cortex-M design has core connected to "bus matrix".
It is up to chip vendor to decide what else is connected to bus matrix.
Yes.
However, there are other things connected before these crossbar
switches, such as tightly-coupled memory (if any).
And the cpu caches
(if any) are on the cpu side of the switches.
Manufacturers also have a
certain amount of freedom of the TCMs and caches, depending on which
core they are using and which licenses they have.
There is a convenient diagram here:
<https://www.electronicdesign.com/technologies/embedded/digital-ics/processors/microcontrollers/article/21800516/cortex-m7-contains-configurable-tightly-coupled-memory>
For me it does not matter if it is ARM design or vendor specific.
Normal internal RAM is accessed via bus matrix, and in MCU-s that
I know about is fast enough so that cache is not needed. So caches
come into play only for flash (and possibly external memory, but
design with external memory probably will be rather large).
Typically you see data caches on faster Cortex-M4 microcontrollers with external DRAM, and it is also standard on Cortex-M7 devices. For the
faster chips, internal SRAM on the AXI bus is not fast enough. For
example, the NXP i.mx RT106x family typically run at 528 MHz core clock,
but the AXI bus and cross-switch are at 133 MHz (a quarter of the
speed). The tightly-coupled memories and the caches run at full core speed.
It seems that vendor do not like to say that they use cache, instead
that use misleading terms like "flash accelerator".
That all depends on the vendor, and on how the flash interface
controller. Vendors do like to use terms that sound good, of course!
So a "cache" of 32 words is going to be part of the flash interface, not >>> a cpu cache
Well, caches never were part of CPU proper, they were part of
memory interface. They could act for whole memory or only for part
that need it (like flash). So I do not understand what "not a cpu
cache" is supposed to mean. More relevant is if such thing act
as a cache, 32 word things almost surely will act as a cache,
8 word thing may be a simple FIFO buffer (or may act smarter
showing behaviour typical of caches).
Look at the diagram in the link I gave above, as an example. CPU caches
are part of the block provided by ARM and are tightly connected to the processor. Control of the caches (such as for enabling them) is done by hardware registers provided by ARM, alongside the NVIC interrupt
controller, SysTick, MPU, and other units (depending on the exact
Cortex-M model).
This is completely different from the small buffers that are often
included in flash controllers or external memory interfaces as
read-ahead buffers or write queues (for RAM), which are as external the processor core as SPI, UART, PWM, ADC, and other common blocks provided
by the microcontroller manufacturer.
(which are typically 16KB - 64KB,
I wonder where you found this figure. Such size is typical for
systems bigger than MCU-s. It could be useful for MCU-s with
flash a on separate die, but with flash on the same die as CPU
much smaller cache is adequate.
Look at the Wikipedia link I gave. Those are common sizes for the
Cortex-M7 (which is pretty high-end), and for the newer generation of Cortex-M35 and Cortex-M5x parts. I have on my desk an RTO1062 with a
600 MHz Cortex-M7, 1 MB internal SRAM, 32 KB I and D caches, and
external QSPI flash.
and only found on bigger
microcontrollers with speeds of perhaps 120 MHz or above). And yes, it
is often fair to call these flash caches "prefetch buffers" or
read-ahead buffers.
Typical code has enough branches that simple read-ahead beyond 8
words is unlikely to give good results. OTOH delivering things
that were accessed in the past and still present in the cache
gives good results even with very small caches.
There are no processors with caches smaller than perhaps 4 KB - it is
simply not worth it.
Read-ahead buffers on flash accesses are helpful,
however, because most code is sequential most of the time. It is common
for such buffers to be two-way, and to have between 16 and 64 bytes per
way.
On 08/09/2024 17:44, Bart wrote:
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
In language like C, the LHS of an assignment is one of four categories: >>>>
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
OK, so what are they?
TM:
Yes, very good. I count four or five, depending on what
differences count as different.
I guess nobody is going to say what those extra categories are, are they?
On 06/09/2024 12:53, Tim Rentsch wrote:
Bart <bc@freeuk.com> writes:
On 05/09/2024 16:21, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
So what exactly is different about the LHS and RHS here:
A = A;
(In BLISS, doing the same thing requires 'A = .A' AIUI; while
'A = A' is also valid, there is a hidden mismatch in indirection
levels between left and right. It is asymmetric while in C it
is symmetric, although seem to disagree on that latter point.)
You seem to miss the point that assigment operator is
fundamentally assymetic.
If you've followed the subthread then you will know that nobody
disputes that assignment reads from side of '=' and writes to the
other.
The symmetry is to do with syntax when the same term appears on
both sides of '=', the type associated with each side, and,
typically, the internal representations too.
Maybe it would help if you would stop thinking in terms of the
word symmetry (clearly assignment is not symmetrical) and instead
think about consistency.
In C, the meaning of an identifier or object-locating expression
depends on where it is in the syntax tree. In some places it
means the address of the object; in other places it means the
contents of whatever is stored in the object.
In a HLL, a named object (ie. a variable name) is nearly always meant
to to refer to an object's value, either its current value or what
will be its new value.
Considering the point of view of a compiler writer, it's easier
to write a compiler for Bliss than for C. In Bliss, upon seeing
an identifier, always simply put its address in a register. If
an object's value needs to be loaded, there will be a '.' to take
the address produced by the sub-expression and fetch the word
stored at that address. On the other hand, in C, upon seeing an
identifier, the compiler needs to consider the context of where
the identifier appears:
You can do the same thing in a C compiler: always load the
address of any identifier associated with the location of
value.
Bart <bc@freeuk.com> writes:
On 08/09/2024 17:44, Bart wrote:
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:OK, so what are they?
In language like C, the LHS of an assignment is one of four categories: >>>>>
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
TM:
Yes, very good. I count four or five, depending on whatdifferences count as different.
I guess nobody is going to say what those extra categories are, are they?
The LHS of an assignment must be a modifiable lvalue. Searching for the
word "lvalue" in the standard's section on expressions yields several
forms not listed above:
- A parenthesized lvalue:
(A) = Y;
- A generic selection whose result expression is an lvalue:
_Generic(0, int: A) = Y;
Not sure why you'd do this.
- X->m, where X is a pointer (you might think of that as the same
category as X.m, but the standard doesn't define the -> operator in
terms of the . operator)
- A compound literal:
int n;
(int){n} = 42;
This assigns a value to a temporary object which is immediately
discarded. I can't think of a valid use for this.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Ben Bacarisse <ben@bsb.me.uk> writes:
[...]
And as for your remarks about typical implementations, does your C
parser /really/ accept an assignment expression on both sides of
an = operator? What does that even look like in the code? I have
written one C parser, contributed to one other and (over the
years) examined at least two more, and none of them do what you
seem to be suggesting is typical.
It wouldn't be surprising to see a parser written so it would
accept (syntactically) a superset of the well-formed inputs
allowed by the language grammar. Any parses not allowed by the
grammar could then be flagged as erroneous in a later semantics
pass.
Yes, that is pretty much what I've seen in more than one C parser.
[...]
I had thought that the standard required support for constants of
extended integer types wider than int, but the wording seems
ambiguous at best.
An unsuffixed decimal constant, for example, has a type that is
the first of int, long int, long long int in which its value can
be represented. But:
If an integer constant cannot be represented by any type in
its list, it may have an extended integer type, if the
extended integer type can represent its value. If all of the
types in the list for the constant are signed, the extended
integer type shall be signed. If all of the types in the list
for the constant are unsigned, the extended integer type shall
be unsigned. If the list contains both signed and unsigned
types, the extended integer type may be signed or unsigned.
If an integer constant cannot be represented by any type in
its list and has no extended integer type, then the integer
constant has no type.
The word "may" in that first sentence seems problematic. It
implies a choice: either a decimal integer constant with a value
of LLONG_MAX+1 is of an extended integer type, or it isn't. One
possible interpretation is that such a constant must be of an
extended integer type if an appropriate type exists. Another is
that it may or may not be of an extended integer type at the whim
of the implementation -- but the standard doesn't say that the
choice is either implementation-defined or unspecified. It just
says "may".
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
Have you tried looking at other uses of the word "may" in the C
standard to see if that sheds some light on the question?
If you have any actual insight to offer, feel free to do so.
On 09/09/2024 02:19, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 08/09/2024 19:13, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 08/09/2024 01:05, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
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.
You're doing a fine job of it!
For me it's one of those languages, like Brainf*ck, which is trivial to >>>>> implement (I've done both), but next to impossible to code in.
I wonder if you really implemented Forth. Did you implement immediate >>>> words? POSTPONE?
I implemented a toy version, with 35 predefined words, that was enough
to implement Fizz Buzz. Then I looked for more examples to try and found >>> they all assumed slightly different sets of built-ins.
OK, so apparently you missed essential part.
I've looked at half a dozen hits for 'forth postpone' and I still don't understand what it does. Apparently something to do with compiled mode.
I wouldn't know enough to confidently implement it or use it.
Another mysterious feature with hard to understand semantics. You did
say this was a very simple language and trivial to implement in a few KB?
My opinion of Forth has gone down a couple of notches; sorry.
(I'm not against all stack-based user-languages; I was quite impressed
by PostScript for example. But then I didn't have to do much in-depth
coding in it.)
On ZX81? I can imagine it being hard! (Someone wanted me to do something >>> on ZX80, but I turned it down. I considered it too much of a toy.)
To give more background, bare ZX81 had 1kB RAM (including video RAM).
You must mean /excluding/ surely? Otherwise there wouldn't be much left
from 1KB!
The first Z80 machine I /made/ had 0.25KB RAM, to which I added 1KB
(actually 1K 6-bit words; two bits unpopulated to save £6), of text-mode video memory.
The second version had 32KB RAM, the same 1K text-mode memory, and 8KB graphics-mode video memory. I was able to write my first compiler on
that one, written using an assembler, which itself was written via a hex editor, and that was written in actual binary. ('Full-stack')
But both only had tape storage so tools were memory-based.
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:46, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit >>>>>>> machines doing otherwise would add useless instructions to object >>>>>>> code. More precisly, really stupid compiler will generate useless >>>>>>> intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly.
But at least some gcc versions needed such declarations. Note
also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables >>>>>> were used in loops where they need to be widened to 64 bits anyway. The >>>>>> new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler >>>>> should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the >>>> range you need), and want it to be as efficient as possible on 32-bit
and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on >>>> 32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can >>>> make code significantly more efficient on 64-bit systems while retaining >>>> efficiency on 32-bit (or even smaller) targets.
Well, I have constraints, some of which are outside of C code.
To resolve contraints I normally use some configure machinery.
If a standard type is exact match for my constraints, then I would
use standard type, possibly adding some fallbacks for pre-standard
systems. But if my constranits differ, I see no advantage in
using more "fancy" standard types compared to old types.
The "fancy" standard types in this case specify /exactly/ what you
apparently want - a type that can deal with at least 32 bits for range,
and is as efficient as possible on different targets. What other
constraints do you have here that make "int_fast32_t" unsuitable?
This routine is part of small library. Good approximation is that
I want a type (posibly depending on target) which will make this
library fast. As I wrote in another message I plan to use 64-bit
units of work on 64-bit targets. I have not plans to use the
library in 8-bit or 16-bit targets, but if I needed them on such
targets I probably would use 16-bit work unit. So, while 32-bit
work unit represents current state, it not correct statement of
intentions. Consequently, 'int_fast32_t' would be as misleading
or more misleading than 'int' ('int' is reasonable indication
that choice of types is not finished, 'int_fast32_t' suggest that
it is proper choice for all targets).
Concerning '<stdint.h>', somewhat worring thing is that several types
there seem to be optional (or maybe where optional in older versions
of the standard). I am not sure if this can be real problem,
but too many times I saw that on various issues developers say
"this in not mandatory, so we will skip it".
<stdint.h> has been required since C99. It has not been optional in any
C standard in the last 25 years. That's a /long/ time - long enough for
most people to be able to say "it's standard".
And almost every C90 compiler also includes <stdint.h> as an extension.
If you manage to find a compiler that is old enough not to have
<stdint.h>, and you need to use it for actual code, it's probably worth
spending the 3 minutes it takes to write a suitable <stdint.h> yourself
for that target.
ATM I do not have standard text handy to check, but AFAIR several
_types_ in <stdint.h> were not required but optional.
David Brown <david.brown@hesbynett.no> writes:
On 09/09/2024 20:46, Kaz Kylheku wrote:
On 2024-09-09, David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:57, Bart wrote:How can we write a program which, in an implementation which has a
On 09/09/2024 17:21, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
C23 doesn't add any new support for 128-bit integers.
So what does _Bitint do with a width of 128 bits?
_BitInt types are not "integer types". Nor is gcc's __int128 type.
__int128 type, outputs "yes" if it is an integer type, otherwise "no"?
I'm not sure such a program, one that can detect either an __int128 that isn't an integer type or an __int128 that is an integer type, is possible.
#include <stdio.h>
int main() {
auto const x = 0x1'0000'0000'0000'0000;
if (x > 0xffff'ffff'ffff'ffff) {
printf("yes\n");
} else {
printf("no\n");
}
}
Of course this uses digit separators, which are a new feature in C23.
If an implementation doesn't have an integer type wider than 64 bits,
then the constant 0x1'0000'0000'0000'0000 has no type, which violates a constraint.
If an implementation does have a integer types wider than 64 bits,
there's no guarantee that it uses the name "__int128". A future gcc
might have a 128-bit (extended) integer type and still have __int128
with its current semantics.
For gcc, this meets Kaz's specification:
#include <stdio.h>
int main(void) {
puts("no");
}
Bart <bc@freeuk.com> wrote:
I've looked at half a dozen hits for 'forth postpone' and I still don't
understand what it does. Apparently something to do with compiled mode.
I wouldn't know enough to confidently implement it or use it.
Silly example 1:
To give more background, bare ZX81 had 1kB RAM (including video RAM).
You must mean /excluding/ surely? Otherwise there wouldn't be much left
from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
But both only had tape storage so tools were memory-based.
I wonder, did you have any ROM? Or battery backed RAM? Otherwise
how tape reading worked? In micros I know about tape reading
was via software, on Spectum this was more than 100 bytes.
It would be rather tedious to key in tape loader via console switches.
Old mainframes had I/O in hardware, but that was rather large
circuitry.
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 16:36, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 08/09/2024 23:34, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
IIUC STM4 series has cache, and some of them are not so big. There
And while microcontrollers sometimes have a limited form of branch >>>>>> prediction (such as prefetching the target from cache), the more
numerous and smaller devices don't even have instruction caches.
Certainly none of them have register renaming or speculative execution. >>>>>
are now several chinese variants of STM32F103 and some of them have
caches (some very small like 32 words, IIRC one has 8 words and it
is hard to decide if this very small cache or big prefetch buffer).
There are different kinds of cache here. Some of the Cortex-M cores
have optional caches (i.e., the microcontroller manufacturer can choose >>>> to have them or not).
<https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization>
I do not see relevent information at that link.
There is a table of the Cortex-M cores, with the sizes of the optional
caches.
Flash memory, flash controller peripherals, external memory interfaces >>>> (including things like QSPI) are all specific to the manufacturer,
rather than part of the Cortex M cores from ARM. Manufacturers can do >>>> whatever they want there.
AFAIK typical Cortex-M design has core connected to "bus matrix".
It is up to chip vendor to decide what else is connected to bus matrix.
Yes.
However, there are other things connected before these crossbar
switches, such as tightly-coupled memory (if any).
TCM is _not_ a cache.
And the cpu caches
(if any) are on the cpu side of the switches.
Caches are attached were system designer thinks they are useful
(and possible). Word "cache" has well-estabished meaning and
ARM (or you) has no right to redefine it.
Manufacturers also have a
certain amount of freedom of the TCMs and caches, depending on which
core they are using and which licenses they have.
There is a convenient diagram here:
<https://www.electronicdesign.com/technologies/embedded/digital-ics/processors/microcontrollers/article/21800516/cortex-m7-contains-configurable-tightly-coupled-memory>
For me it does not matter if it is ARM design or vendor specific.
Normal internal RAM is accessed via bus matrix, and in MCU-s that
I know about is fast enough so that cache is not needed. So caches
come into play only for flash (and possibly external memory, but
design with external memory probably will be rather large).
Typically you see data caches on faster Cortex-M4 microcontrollers with
external DRAM, and it is also standard on Cortex-M7 devices. For the
faster chips, internal SRAM on the AXI bus is not fast enough. For
example, the NXP i.mx RT106x family typically run at 528 MHz core clock,
but the AXI bus and cross-switch are at 133 MHz (a quarter of the
speed). The tightly-coupled memories and the caches run at full core speed.
OK, if you run core at faster clock than the bus matrix, then cache
attached on core side make a lot of sense. And since cache has to
compensate for lower bus speed it must be resonably large.
But
if you look at devices where bus matrix runs at the same clock
as the core, then it makes sense to put cache on the other side.
It seems that vendor do not like to say that they use cache, instead
that use misleading terms like "flash accelerator".
That all depends on the vendor, and on how the flash interface
controller. Vendors do like to use terms that sound good, of course!
So a "cache" of 32 words is going to be part of the flash interface, not >>>> a cpu cache
Well, caches never were part of CPU proper, they were part of
memory interface. They could act for whole memory or only for part
that need it (like flash). So I do not understand what "not a cpu
cache" is supposed to mean. More relevant is if such thing act
as a cache, 32 word things almost surely will act as a cache,
8 word thing may be a simple FIFO buffer (or may act smarter
showing behaviour typical of caches).
Look at the diagram in the link I gave above, as an example. CPU caches
are part of the block provided by ARM and are tightly connected to the
processor. Control of the caches (such as for enabling them) is done by
hardware registers provided by ARM, alongside the NVIC interrupt
controller, SysTick, MPU, and other units (depending on the exact
Cortex-M model).
This is completely different from the small buffers that are often
included in flash controllers or external memory interfaces as
read-ahead buffers or write queues (for RAM), which are as external the
processor core as SPI, UART, PWM, ADC, and other common blocks provided
by the microcontroller manufacturer.
The disscussion started about possible interaction of caches
and virtual function dispatch.
This interaction does not depend
on you calling it cache. It depends on cache hits/misses,
their cost and possible eviction. And actually small caches
can give "interesting" behaviour: with small code footprint there
may be 100% hit ratio, but one extra memory reference may lead
to significant misses. And even small caches behave differently
then simple buffers.
(which are typically 16KB - 64KB,
I wonder where you found this figure. Such size is typical for
systems bigger than MCU-s. It could be useful for MCU-s with
flash a on separate die, but with flash on the same die as CPU
much smaller cache is adequate.
Look at the Wikipedia link I gave. Those are common sizes for the
Cortex-M7 (which is pretty high-end), and for the newer generation of
Cortex-M35 and Cortex-M5x parts. I have on my desk an RTO1062 with a
600 MHz Cortex-M7, 1 MB internal SRAM, 32 KB I and D caches, and
external QSPI flash.
OK, as I wrote it makes sense for them. But for smaller machines
much smaller caches may be adequate.
and only found on bigger
microcontrollers with speeds of perhaps 120 MHz or above). And yes, it >>>> is often fair to call these flash caches "prefetch buffers" or
read-ahead buffers.
Typical code has enough branches that simple read-ahead beyond 8
words is unlikely to give good results. OTOH delivering things
that were accessed in the past and still present in the cache
gives good results even with very small caches.
There are no processors with caches smaller than perhaps 4 KB - it is
simply not worth it.
Historicaly there were processors with small caches. 256B in
Motorla chips and I think smaller too. It depends on the whole
design.
Currently for "big" processors really small caches seem
to make no sense. Microconrollers have their own constaints.
Manufacturer may decide that cache giving 10% average improvement
is not worth uncertainilty of execution time. Or may decide that
small cache is the cheapest way to get better benchmark figures.
Read-ahead buffers on flash accesses are helpful,
however, because most code is sequential most of the time. It is common
for such buffers to be two-way, and to have between 16 and 64 bytes per
way.
If you read carefully description of STM "flash accelerator" it is
clear that this is classic cache, with line size matched to flash,
something like 2-set associativity, conflicts and eviction.
Historically there were variations, some caches only cache targets
of jumps and use prefetch buffer for linear code. Such caches
can be effective at very small size.
On 10/09/2024 05:40, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
I've looked at half a dozen hits for 'forth postpone' and I still don't
understand what it does. Apparently something to do with compiled mode.
I wouldn't know enough to confidently implement it or use it.
Silly example 1:
<snip long article about Forth>
For a non-advocate of Forth you have a lot to say about it!
I've archived your post for if/when I get back to that project. (If I
ever use Forth, it'll be on my own terms. I just tried it to see if it's still breathing:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
; 2 2 + .4
; bye
So it looks like I already thought of it as my version.)
To give more background, bare ZX81 had 1kB RAM (including video RAM).
You must mean /excluding/ surely? Otherwise there wouldn't be much left
from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
So that meagre 1KB had to be shared?
How can we write a program which, in an implementation which has a
__int128 type, outputs "yes" if it is an integer type, otherwise "no"?
I'd like the program to have well-defined behavior, under the test
assumption (that a documented, conforming extension is provided in
the form of __int128).
Integer types don't have to have their own matching constants.
The types char and short don't have their own constants; they
borrow int. Library support can be missing, in regard to
formatting __int128 to text and back.
The __int128 type better support all integer arithmetic, though:
and there should be conversion rules regarding when __int128 is
opposite to an operand of a different type. An __int128 should be
usable as a displacement in pointer arithmetic. It should be
possible to switch() on an __int128, even if a constant cannot be
expressed for every possible value.
On 10/09/2024 12:52, Bart wrote:
I've archived your post for if/when I get back to that project. (If I
ever use Forth, it'll be on my own terms. I just tried it to see if
it's still breathing:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
> 2 2 + .
4
> bye
But it handle :
: 2 1 ;
2 2 + .
and get the answer 2 ? :-)
So it looks like I already thought of it as my version.)
To give more background, bare ZX81 had 1kB RAM (including video RAM). >>>>You must mean /excluding/ surely? Otherwise there wouldn't be much left >>>> from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
So that meagre 1KB had to be shared?
Yes. 24 lines of 32 characters took 768 bytes, and the OS and BASIC
took another 125 bytes. There was not a lot of space for user code and data!
On 10/09/2024 00:04, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:46, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit >>>>>>>> machines doing otherwise would add useless instructions to object >>>>>>>> code. More precisly, really stupid compiler will generate useless >>>>>>>> intructions even with my declarations, really smart one will
notice that variables fit in 32-bits and optimize accordingly. >>>>>>>> But at least some gcc versions needed such declarations. Note >>>>>>>> also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision),
you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables >>>>>>> were used in loops where they need to be widened to 64 bits anyway. The >>>>>>> new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler >>>>>> should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would
destroy carry flag, so there must be code using value of carry in
register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the >>>>> range you need), and want it to be as efficient as possible on 32-bit >>>>> and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on >>>>> 32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can >>>>> make code significantly more efficient on 64-bit systems while retaining >>>>> efficiency on 32-bit (or even smaller) targets.
Well, I have constraints, some of which are outside of C code.
To resolve contraints I normally use some configure machinery.
If a standard type is exact match for my constraints, then I would
use standard type, possibly adding some fallbacks for pre-standard
systems. But if my constranits differ, I see no advantage in
using more "fancy" standard types compared to old types.
The "fancy" standard types in this case specify /exactly/ what you
apparently want - a type that can deal with at least 32 bits for range,
and is as efficient as possible on different targets. What other
constraints do you have here that make "int_fast32_t" unsuitable?
This routine is part of small library. Good approximation is that
I want a type (posibly depending on target) which will make this
library fast. As I wrote in another message I plan to use 64-bit
units of work on 64-bit targets. I have not plans to use the
library in 8-bit or 16-bit targets, but if I needed them on such
targets I probably would use 16-bit work unit. So, while 32-bit
work unit represents current state, it not correct statement of
intentions. Consequently, 'int_fast32_t' would be as misleading
or more misleading than 'int' ('int' is reasonable indication
that choice of types is not finished, 'int_fast32_t' suggest that
it is proper choice for all targets).
So you really want to say "at least 16 bits, as efficiently as possible"
- the type then is "int_fast16_t". This should be 32-bit or 64-bit on
most architectures bigger than 16-bit - unless the target is actually
more efficient at handling 16-bit (such as the original 68000).
If something like that is not sufficient, because you want more
flexibility, then consider making a typedef with a name you find
suitable, and a using that. You can have pre-processor conditional compilation to handle known cases, and fall back to int_fast16_t otherwise.
The answer to "I don't know if this is the best choice on all targets, including ones I haven't tried" is /not/ "I'll use this type that I know
is not the most efficient on targets that I /have/ tried".
On 10/09/2024 12:55, David Brown wrote:
On 10/09/2024 12:52, Bart wrote:
I've archived your post for if/when I get back to that project.
(If I ever use Forth, it'll be on my own terms. I just tried it to
see if it's still breathing:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
> 2 2 + .
4
> bye
But it handle :
: 2 1 ;
2 2 + .
and get the answer 2 ? :-)
Apparently:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
> : 2 1 ;
> 2 2 + .
2
>
So it looks like I already thought of it as my version.)
To give more background, bare ZX81 had 1kB RAM (including video
RAM).
You must mean /excluding/ surely? Otherwise there wouldn't be
much left from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
So that meagre 1KB had to be shared?
Yes. 24 lines of 32 characters took 768 bytes, and the OS and
BASIC took another 125 bytes. There was not a lot of space for
user code and data!
An OS /and/ BASIC takes 125 bytes? You can't really complain about
bloat here! Windows is probably 20GB and doesn't have Basic.
David Brown <david.brown@hesbynett.no> wrote:
On 10/09/2024 00:04, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 18:46, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 05:04, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
On 09/09/2024 01:29, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
No. It is essential for efficiency to have 32-bit types. On 32-bit >>>>>>>>> machines doing otherwise would add useless instructions to object >>>>>>>>> code. More precisly, really stupid compiler will generate useless >>>>>>>>> intructions even with my declarations, really smart one will >>>>>>>>> notice that variables fit in 32-bits and optimize accordingly. >>>>>>>>> But at least some gcc versions needed such declarations. Note >>>>>>>>> also that my version makes clear that there there is
symmetry (everything should be added using 64-bit precision), >>>>>>>>> you depend on promotion rules which creates visual asymetry
are requires reasoning to realize that meaning is symetric.
Your posted code used 64-bit aritmetic. The xext and c 32-bit variables
were used in loops where they need to be widened to 64 bits anyway. The
new value of c is set from a 32-bit result.
Well, at C level there is 64-bit type. The intent is that C compiler >>>>>>> should notice that the result is 32-bit + carry flag. Ideally
compiler should notice that c has only one bit and can keep it
in carry flag. On i386 comparison needed for loop control would >>>>>>> destroy carry flag, so there must be code using value of carry in >>>>>>> register and code to save carry to register. But one addition
of highs parts can be skipped. On 32-bit ARM compiler can use
special machine istructions and actually generated code which
is close to optimal.
When you have a type that you want to be at least 32 bits (to cover the >>>>>> range you need), and want it to be as efficient as possible on 32-bit >>>>>> and 64-bit machines (and 16-bit and 8-bit, still found in
microcontrollers), use "int_fast32_t". On x86-64 it will be 64-bit, on >>>>>> 32-bit systems it will be 32-bit. Use of the [u]int_fastNN_t types can >>>>>> make code significantly more efficient on 64-bit systems while retaining >>>>>> efficiency on 32-bit (or even smaller) targets.
Well, I have constraints, some of which are outside of C code.
To resolve contraints I normally use some configure machinery.
If a standard type is exact match for my constraints, then I would
use standard type, possibly adding some fallbacks for pre-standard
systems. But if my constranits differ, I see no advantage in
using more "fancy" standard types compared to old types.
The "fancy" standard types in this case specify /exactly/ what you
apparently want - a type that can deal with at least 32 bits for range, >>>> and is as efficient as possible on different targets. What other
constraints do you have here that make "int_fast32_t" unsuitable?
This routine is part of small library. Good approximation is that
I want a type (posibly depending on target) which will make this
library fast. As I wrote in another message I plan to use 64-bit
units of work on 64-bit targets. I have not plans to use the
library in 8-bit or 16-bit targets, but if I needed them on such
targets I probably would use 16-bit work unit. So, while 32-bit
work unit represents current state, it not correct statement of
intentions. Consequently, 'int_fast32_t' would be as misleading
or more misleading than 'int' ('int' is reasonable indication
that choice of types is not finished, 'int_fast32_t' suggest that
it is proper choice for all targets).
So you really want to say "at least 16 bits, as efficiently as possible"
- the type then is "int_fast16_t". This should be 32-bit or 64-bit on
most architectures bigger than 16-bit - unless the target is actually
more efficient at handling 16-bit (such as the original 68000).
As I wrote I have other constrants that are likely to favour 32 bits
on 32-bit machines and 64-bit on 64-bit machines. As you noticed "int_fast16_t" may be 16 bit even if native word size is bigger.
And concerning proper size for "int_fast16_t", it is enough that
CPU performs scalar operations with the same speed regardless
of size of operands (including mixed operands). Then there is
no reason to favour bigger size. And there are reasons to favour
smaller size, like cache use or opportunities for autovectorization.
So I would make "int_fast16_t" into 16-bit type on such a machine.
So, I could use "int_fastX_t" with X determined by my constranits,
but assuming "int_fastX_t = int_fast16_t" would be a latent bug.
If something like that is not sufficient, because you want more
flexibility, then consider making a typedef with a name you find
suitable, and a using that. You can have pre-processor conditional
compilation to handle known cases, and fall back to int_fast16_t otherwise.
As I wrote, I want type to be determined by configuration machinery.
And configuration machinery will generate appropriate typedefs.
The answer to "I don't know if this is the best choice on all targets,
including ones I haven't tried" is /not/ "I'll use this type that I know
is not the most efficient on targets that I /have/ tried".
Actually, main use case is when I know _a lot_ about targets. Concerning other targets, they are "to do", when I came to them, standard types
_may_ give useful baseline.
And to put a bit differently what I wrote previously, consider
'int' in current code as big shouting "FIXME". I have different
things to fix first, and I want a proper fix, so delay with this
one.
On 10/09/2024 12:55, David Brown wrote:
On 10/09/2024 12:52, Bart wrote:
I've archived your post for if/when I get back to that project. (If I
ever use Forth, it'll be on my own terms. I just tried it to see if
it's still breathing:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
> 2 2 + .
4
> bye
But it handle :
: 2 1 ;
2 2 + .
and get the answer 2 ? :-)
Apparently:
c:\qapps>qq forth
Bart-Forth
Type bye to stop
> : 2 1 ;
> 2 2 + .
2
>
So it looks like I already thought of it as my version.)
To give more background, bare ZX81 had 1kB RAM (including video RAM). >>>>>You must mean /excluding/ surely? Otherwise there wouldn't be much
left
from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
So that meagre 1KB had to be shared?
Yes. 24 lines of 32 characters took 768 bytes, and the OS and BASIC
took another 125 bytes. There was not a lot of space for user code
and data!
An OS /and/ BASIC takes 125 bytes? You can't really complain about bloat here! Windows is probably 20GB and doesn't have Basic.
On 08/09/2024 17:44, Bart wrote:
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:OK, so what are they?
In language like C, the LHS of an assignment is one of four categories: >>>>
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
TM:
Yes, very good. I count four or five, depending on whatdifferences count as different.
I guess nobody is going to say what those extra categories are, are
they?
It's bad form to call somebody out on something but then refuse to tell
them exactly what they've got wrong or have missed out.
3, 4, or maybe 5 mysterious categories of LHS assignment terms that I have never been encountered in a million lines of C code I've processed, but nobody is willing to say what they are?
I sense a wind-up.
I can think of at least one expression form for X that contradicts thisExample?
claim.
Nothing here either.
Bart <bc@freeuk.com> writes:
Someone objects that you can't in general apply & to arbitrary, unnamed,
transient, intermediate values such as 'a + 1'.
or even just 1.
I showed how you could do that using anonymous compound literals which
avoids having to create an explicit named temporary which in standard C
would need to be outside of that assignment call.
But you apparently have a problem it.
Because you called it a reference to a+1. The reference is to an
object.
I don't know if you were deliberately twisting the term because you are
now 100% committed to some false symmetry in assignments, or whether you
are just very loose in your use of terms, but rvalue expressions (C does
not really use the term, but it's better than non-lvalue expressions)
can't have "references" to them. That was all that Waldek Hebisch was saying. Did you think for a second that he did not know that if you put
an int value into an object you can take the pointer to that object?
On 08/09/2024 17:14, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 07/09/2024 02:44, Waldek Hebisch wrote:Is there no part of C you can't misrepresent?
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:
Is nothing I write that you will take issue with?
#include <stdio.h>This is simply an anonymous object. You could have used a named object
void assign(int* lhs, int* rhs) {
*lhs = *rhs;
}
int main(void) {
int a=20;
assign(&a, &(int){a+1});
and it wold not have been any further from being a "reference to a+1".
I suggested a 'assign()' function could have balanced parameters by requiring:
asssign(&A, &B);
Someone objects that you can't in general apply & to arbitrary, unnamed, transient, intermediate values such as 'a + 1'.
I showed how you could do that using anonymous compound literals which
avoids having to create an explicit named temporary which in standard C
would need to be outside of that assignment call.
But you apparently have a problem it.
Or more likely you have a problem with me.
Bart <bc@freeuk.com> writes:
On 08/09/2024 17:44, Bart wrote:
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:OK, so what are they?
In language like C, the LHS of an assignment is one of four categories: >>>>>
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
TM:
Yes, very good. I count four or five, depending on whatdifferences count as different.
I guess nobody is going to say what those extra categories are, are
they?
Sorry, I was busy. I see KT as given a good summary (though I was not counting forms in parentheses).
It's bad form to call somebody out on something but then refuse to tell
them exactly what they've got wrong or have missed out.
3, 4, or maybe 5 mysterious categories of LHS assignment terms that I have >> never been encountered in a million lines of C code I've processed, but
nobody is willing to say what they are?
I sense a wind-up.
You have implemented a C compiler. The wind-up I sensed was your giving
out misinformation, but I'll just have to take your word for it that
you've been arguing about assignments without know what constitutes an
lvalue expression.
But when I didn't answer soon enough, surely you could have just looked
in any good C reference to find all the expression forms that are
lvalues.
I can think of at least one expression form for X that contradicts this >>>> claim.Example?
Nothing here either.
f().m where f returns a struct.
David Brown <david.brown@hesbynett.no> writes:
On 09/09/2024 22:16, Keith Thompson wrote:[...]
I felt free to use C23 because we were already talking about the
support, or lack thereof, of 128-bit integers in C23, and also
_BitInt. And on a platform that supported 128-bit integer types,
"auto" would pick a 128-bit type here, while other platforms would
pick a 64-bit type.
That last assumption turns out not to be correct.
If an implementation doesn't have an integer type wider than 64 bits,
then the constant 0x1'0000'0000'0000'0000 has no type, which violates
a constraint.
Indeed. Conforming implementations need to give an error or warning
here. But some will also then map it to a 64-bit value (I'd usually
expect 0, but 0xffff'ffff or 0x7fff'ffff might also be used) and
continue compilation. That gives a program with the desired
behaviour. (For my own uses, I would pick flags that gave an error on
such code.)
gcc 14.2.0 gives it type int and value 0. I think what's happening is
that the value wraps around, and then the compiler determines its type
based on that. It also issues a warning.
Since the code violates a constraint, you can't rely on its behavior.
clang 18.1.0 rejects it.
If an implementation does have a integer types wider than 64 bits,
there's no guarantee that it uses the name "__int128". A future gcc
might have a 128-bit (extended) integer type and still have __int128
with its current semantics.
If an implementation had a standard integer type of 128 bits, then I
expect "long long int" would be the name (unless it also had even
bigger types!), but for extended integer types it could be many
different things. It is very likely, however, that it would also be
in <stdint.h> as int128_t.
I would probably expect most compilers to keep long long at 64 bits, to
cater to existing code that makes that assumption. That's what gcc did
when it added __int128 (which is *almost* an extended integer type, but
they don't call it one).
Do you know of any implementations that do have 128-bit integer types?
In particular, any that have a compiler on godbolt.org?
None that I know of.
On 10/09/2024 15:15, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 08/09/2024 17:44, Bart wrote:Sorry, I was busy. I see KT as given a good summary (though I was not
On 08/09/2024 16:39, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:OK, so what are they?
In language like C, the LHS of an assignment is one of four categories: >>>>>>
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I can think of three others. There may be more.
TM:
Yes, very good. I count four or five, depending on whatdifferences count as different.
I guess nobody is going to say what those extra categories are, are
they?
counting forms in parentheses).
It's bad form to call somebody out on something but then refuse to tellYou have implemented a C compiler. The wind-up I sensed was your giving
them exactly what they've got wrong or have missed out.
3, 4, or maybe 5 mysterious categories of LHS assignment terms that I have >>> never been encountered in a million lines of C code I've processed, but
nobody is willing to say what they are?
I sense a wind-up.
out misinformation, but I'll just have to take your word for it that
you've been arguing about assignments without know what constitutes an
lvalue expression.
But when I didn't answer soon enough, surely you could have just looked
in any good C reference to find all the expression forms that are
lvalues.
f().m where f returns a struct.I can think of at least one expression form for X that contradicts this >>>>> claim.Example?
Nothing here either.
f().m is allowed with mcc and tcc compilers (but it doesn't do anything useful). It's not classed as an lvalue by gcc.
By "the LHS of an assignment", and "X is a term of any complexity" I imply those X's forming valid LHS terms.
An X used as X[i]=Y, *X=Y, or X.m=Y, or even any of the Y's, could be rejected for lots of reasons. That X isn't an array, pointer or struct for example.
(BTW I've now counted the different categories of my own languages are
there are about 15 in all that can be used as assignment targets. A lot are just terms that can appear as rvalues, that can also appear as lvalues.
For example a 'switch' statement, which standard C doesn't even allow as an rvalue.
Sorry, did your remark above suggest I don't know what an lvalue is?
Maybe
it's a miracle all this stuff works then!)
And yes I'm still committed to that symmetry. I'ved used it for countless language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to work.
You can't really use that to bash me about the head with and maintain that all my ideas about language implementation are wrong because C views assignment in its own idiosyncratic manner.
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is?
That seemed like the obvious explanation for the incorrect information
you gave. Did you post it /knowing/ what other kinds of things are
lvalues in C just to confuse people?
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless
language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to >> work.
You can't really use that to bash me about the head with and maintain that >> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
I don't want to bash you about the head, but what C says about
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You /know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same".
Tim suggests that there is communication failure here -- that you have
not expressed what you mean clearly enough. That may be so, but I can't
see how to interpret what you've written in any other way.
On 11/09/2024 01:22, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>> language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to >>> work.
You can't really use that to bash me about the head with and maintain that >>> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
I don't want to bash you about the head, but what C says about
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You
/know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same".
I've listed the aspects that I said are the same.
That is, if something is a legal LHS term, then its syntax, and its
type, are identical to that term appearing on the RHS.
On 2024-09-11, Bart <bc@freeuk.com> wrote:
On 11/09/2024 01:22, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>>> language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to
work.
You can't really use that to bash me about the head with and maintain that >>>> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
I don't want to bash you about the head, but what C says about
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You
/know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same".
I've listed the aspects that I said are the same.
That is, if something is a legal LHS term, then its syntax, and its
type, are identical to that term appearing on the RHS.
But the converse isn't true.
"a = b" is a valid RHS term, yet isn't
always valid on the LHS without parentheses on it;
and this isn't
something that is due to a glitch in the ISO C grammar that is removable
by a compiler implementor, since a = b = c means a = (b = c).
Assignment is syntactially symmetric at the AST level only, where
parentheses don't matter any more.
It's possible to generate an AST that has an '=' node with any kind
of expressions as left and right children, and then check it for
valid left sides as a matter of syntax.
If the surface language before the AST is some Lisp-like, then
the symmetry extends to that syntax also, which can just be (set <expr> <expr>); it parses regardless of what the expressions are.
Unless you take symmetry too literally: obviously (set <expr-1>
<expr-2>) is not symmetric with regard to (<expr-2> <expr-1> set). :)
Not to mention (<2-rpxe> <1-rpxe> tes). :)
The "assignment is symmetric at the syntax level" is a kind of Lisp
mindset, which has banished precedence and associativity.
In Common Lisp (and some other dialects), the application program
can extend the repertoire of what kind of expression is a "place";
i.e. can appear as the left argument of an assignment operator,
and be used in other related operators.
In Common Lisp, I can make (setf (+ a 1) 42) do something. For
instance, I can have it set a to 41, so that (+ a 1) yields 42.
Note that if I chose this specific semantics, it will still not allow
(setf (+ 2 2) 42). The + expression will have to contain at least one
term that is itself a place.
(Perhaps we can choose the leftmost
place as the one to operate on so that (setf (+ a b) ...) will
operate on a).
I belong to the Lisp mindset, so I tend to agree with the basic gist
of your idea. Tou've chosen to work in languages where your intuitions
do not work out to being literally true, though. :)
ATM I do not have standard text handy to check, but AFAIR several
_types_ in <stdint.h> were not required but optional.
On 11/09/2024 01:22, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>> language implementations. C is little different other than it has aI don't want to bash you about the head, but what C says about
700-page standard that suggests a recommended model of how it's supposed to >>> work.
You can't really use that to bash me about the head with and maintain that >>> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You
/know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same".
I've listed the aspects that I said are the same.
That is, if something is a legal LHS term, then its syntax, and its type,
are identical to that term appearing on the RHS.
(And by its type, I mean its base type. So given 'int a,b; a=b;', I'm
talking about 'int' not 'int*'.)
There can additionally be similarities within internal representations.
Tim suggests that there is communication failure here -- that you have
not expressed what you mean clearly enough. That may be so, but I can't
see how to interpret what you've written in any other way.
Or people simply can't grasp what I'm saying. I've given a million
examples of identical LHSs and RHSs,
and they insist on saying they're
asymmmetric (while also insisting that it's the A=.A of BLISS that has
true symmetry!).
I acknowledge that LHSs are written and RHSs are read (and also that, while A=A has true reflective symmetry, B=B doesn't, if that's what bothers
some).
On many cpus, using sizes smaller than the full register size means
doing sign extensions or masking operations at various times - thus full
size register operations can often be more efficient. On such systems
you will find that int_fast16_t is 32-bit or 64-bit, according to the register width. On other cpus, some common ALU operations on full-size operands can be slower than for smaller operands (such as on the 68000).
There, int_fast16_t will be 16-bit.
Compiler authors know what will usually be faster on the target. There
will always be some exceptions (division is usually faster on smaller operands, for example). But if you don't know the target - as is the
case of portable code - the compiler will usually make a better choice
here than you would.
On 11/09/2024 01:02, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is?That seemed like the obvious explanation for the incorrect information
you gave. Did you post it /knowing/ what other kinds of things are
lvalues in C just to confuse people?
Which incorrect explanation was that?
I merely said that LHSs of assigments fall into these categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
Clearly I mean VALID LHSs, otherwise they wouldn't be LHSs of assignments!
I've since learnt about a couple of other possible categories; one is with compound literals like '(int){42} = 0'.
(I don't count (A), ((A)) etc as a
separate category; come on!)
The other is 'X.m' but when .m is a bitfield;
although this has the same
same syntax as above, internally it's somewhat different.
(My C compiler
treats bitfields as ordinary members.)
I acknowledge that allowing 'F().m = Y' is wrong;
I might get around to
fixing it one day.
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:22, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>>> language implementations. C is little different other than it has aI don't want to bash you about the head, but what C says about
700-page standard that suggests a recommended model of how it's supposed to
work.
You can't really use that to bash me about the head with and maintain that >>>> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You
/know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same".
I've listed the aspects that I said are the same.
And you've stated that there are differences but of course you haven't
listed them (as far as I can tell).
That is, if something is a legal LHS term, then its syntax, and its type,
are identical to that term appearing on the RHS.
And you have /not/ stated, though you know it perfectly well, that the reverse does /not/ apply -- that many "legal" RHS expressions can't
appear, legally, on the LHS.
Or people simply can't grasp what I'm saying. I've given a million
examples of identical LHSs and RHSs,
But your mistake is not that there are not millions of identical
(looking) LH sides and RH sides. No one disputes that. But in reply to
my statement that what is /required/ on both sides is not the same, you
said "I would argue that it is exactly the same".
On 10/09/2024 15:24, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I don't know if you were deliberately twisting the term because you are
now 100% committed to some false symmetry in assignments, or whether you
are just very loose in your use of terms, but rvalue expressions (C does
not really use the term, but it's better than non-lvalue expressions)
can't have "references" to them. That was all that Waldek Hebisch was
saying. Did you think for a second that he did not know that if you put
an int value into an object you can take the pointer to that object?
He took the symmetry I was claiming for assignment, and created an
asymmetric counter-example where the dereferencing part of the LHS was
moved into a function, necessitating the creation of a discrete
reference for the LHS.
I created a counter-counter-example where dereferences on both sides
were moved into the function, so restoring the symmetry.
And yes I'm still committed to that symmetry. I'ved used it for
countless language implementations. C is little different other than it
has a 700-page standard that suggests a recommended model of how it's supposed to work.
You can't really use that to bash me about the head with and maintain
that all my ideas about language implementation are wrong because C
views assignment in its own idiosyncratic manner.
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:02, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is?That seemed like the obvious explanation for the incorrect information
you gave. Did you post it /knowing/ what other kinds of things are
lvalues in C just to confuse people?
Which incorrect explanation was that?
I merely said that LHSs of assigments fall into these categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
Yes, that incorrect explanation.
Clearly I mean VALID LHSs, otherwise they wouldn't be LHSs of assignments! >>
I've since learnt about a couple of other possible categories; one is with >> compound literals like '(int){42} = 0'.
Along with (a) _Generic expressions (where the selected arm is an
lvalue)
and (b) expressions of the form X->m.
(I don't count (A), ((A)) etc as a
separate category; come on!)
Don't give me "come on!". I was counting forms in the same way that you
were when I said I could think of three more. I was not counting parentheses.
The other is 'X.m' but when .m is a bitfield;
What makes X.m = Y, where m is a bitfield, an extra category? It fits
the X.m = Y pattern perfectly well.
although this has the same
same syntax as above, internally it's somewhat different.
Your categories were syntactic. You were describing forms.
David Brown <david.brown@hesbynett.no> wrote:
On many cpus, using sizes smaller than the full register size means
doing sign extensions or masking operations at various times - thus full
size register operations can often be more efficient. On such systems
you will find that int_fast16_t is 32-bit or 64-bit, according to the
register width. On other cpus, some common ALU operations on full-size
operands can be slower than for smaller operands (such as on the 68000).
There, int_fast16_t will be 16-bit.
Compiler authors know what will usually be faster on the target. There
will always be some exceptions (division is usually faster on smaller
operands, for example). But if you don't know the target - as is the
case of portable code - the compiler will usually make a better choice
here than you would.
BTW, I just played with Clang 18 on 64-bit FreeBSD. It has 32-bit int_fast16_t. gcc in Linux makes it 64-bit. Who is right?
Bart <bc@freeuk.com> wrote:
On 10/09/2024 15:24, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
I don't know if you were deliberately twisting the term because you are
now 100% committed to some false symmetry in assignments, or whether you >>> are just very loose in your use of terms, but rvalue expressions (C does >>> not really use the term, but it's better than non-lvalue expressions)
can't have "references" to them. That was all that Waldek Hebisch was
saying. Did you think for a second that he did not know that if you put >>> an int value into an object you can take the pointer to that object?
He took the symmetry I was claiming for assignment, and created an
asymmetric counter-example where the dereferencing part of the LHS was
moved into a function, necessitating the creation of a discrete
reference for the LHS.
I created a counter-counter-example where dereferences on both sides
were moved into the function, so restoring the symmetry.
Really not. My point was about conceptual model and important
part is to keep such model simple. You used complex language
construct which defeats the purose of conceptual model.
And yes I'm still committed to that symmetry. I'ved used it for
countless language implementations. C is little different other than it
has a 700-page standard that suggests a recommended model of how it's
supposed to work.
You can't really use that to bash me about the head with and maintain
that all my ideas about language implementation are wrong because C
views assignment in its own idiosyncratic manner.
Well, gcc implements assignment in similar way to you (or at least
did that in the past, I have not checked if recent version do the
same). To be more precise, gcc parser when seeing a variable
creates read reference to this variable. When parser realizes that
already recognized part of expression is the left hand side of an
assignment it converts it to write access. So your approach is
no worse than gcc. But it creates troubles, process of changing subexpression with read references into write access is more
complicated than replacing read instruction by write instruction.
One, they need to recognize things which are invalid.
Second,
from the very beginning gcc transforms its intermediate representation.
But some transforms valid on right hand side are invalid on the
left hand side, so gcc needs to postpone them and do later.
Third, intermdiate form contains extra information and that
needs to be handled later.
So your (and gcc) approach is: "let us pretend that assigment
operator is symmetric and fix asymetry (that is left hand side)
later". That works, and I can imagine good reasons to proceed
that way. Or intead of "fixing" one can first generate intemediate
for and then pass right hand side to "right hand side code
generator" and left hand side to "left hand side code generator".
One way or another, in this approach left hand side of assigment
must be handled differently than right hand side. But saying that
assignment operator is really symmetric is wrong. Different
treatement treatment of sizes shows this. And once you accept
that assignment operator is asymetric, then in Bliss you can handle
both sides in exactly the same way. In C, there is implict
lvalue convertion
Bart <bc@freeuk.com> writes:
[...]
It's not that complicated, not with C anyway.
Because in C, if you take the 3-4 categories of LHS in assignments
(ignore the esoteric ones, and [] and . are really the same), there is
only one top-level lvalue node to consider.
I agree, there's only one thing to consider. The LHS of an
assignment is a modifiable lvalue.
We've spent a lot of time arguing about how any "categories" there
are for the LHS of an assignment. If I recall correctly, the whole
thing started when you stated that "the LHS of an assignment is
one of four categories", leading to a debate about whether four
is the correct number.
Enumerating the kinds of expressions that can be modifiable
lvalues is interesting, I suppose, but are A and (A) in different "categories"? Is it important to count generic selections and
compound literals?
Who cares, and why?
And yet, I was doing that in the 1980s on my toy compilers. So that's
not that hard either.
Ok, it's not that hard to implement things that are not valid C.
But it creates troubles, process of changing
subexpression with read references into write access is more
complicated than replacing read instruction by write instruction.
One, they need to recognize things which are invalid.
What exactly /is/ lvalue conversion? What is converted to what?
This is specified in the C standard (6.3.2.1p2 in C11, 6.3.3.1p2
in C23 drafts). I suggest you read it.
An lvalue (an expression that designates an object) is converted
to the value stored in the designated object. This conversion
(adjustment) does not occur on the LHS of an assignment or in
several other contexts.
It might have been clearer to say that the expression is adjusted
from an expression that designates an object (an lvalue) to an
expression that yields the value of that object (not an lvalue)
Note that this is not a run-time conversion,
like a conversion of an
integer value to a floating-point value.
On 10/09/2024 05:40, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
I've looked at half a dozen hits for 'forth postpone' and I still don't
understand what it does. Apparently something to do with compiled mode.
I wouldn't know enough to confidently implement it or use it.
Silly example 1:
<snip long article about Forth>
For a non-advocate of Forth you have a lot to say about it!
To give more background, bare ZX81 had 1kB RAM (including video RAM).
You must mean /excluding/ surely? Otherwise there wouldn't be much left
from 1KB!
There is not much left. But there is a trick: boundary between
video memory and rest is movable, as you fill other part available
screen area shrinks. It does not take long time to have machine
completely filled out with no free space left (and IIRC some
small screen area for out of memory message)
So that meagre 1KB had to be shared?
The Z80 designs I did (for my own use and when I had a job developing commercial machines) never used used tricks like that**.
I never had to work within very tight pricing, but I would have
considered such a machine unviable other than for experimenting.
On 12/09/2024 01:59, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On many cpus, using sizes smaller than the full register size means
doing sign extensions or masking operations at various times - thus full >>> size register operations can often be more efficient. On such systems
you will find that int_fast16_t is 32-bit or 64-bit, according to the
register width. On other cpus, some common ALU operations on full-size
operands can be slower than for smaller operands (such as on the 68000). >>> There, int_fast16_t will be 16-bit.
Compiler authors know what will usually be faster on the target. There
will always be some exceptions (division is usually faster on smaller
operands, for example). But if you don't know the target - as is the
case of portable code - the compiler will usually make a better choice
here than you would.
BTW, I just played with Clang 18 on 64-bit FreeBSD. It has 32-bit
int_fast16_t. gcc in Linux makes it 64-bit. Who is right?
Technically, both are right - implementations can use any integer type
of at least 16 bits here, whatever they think is fastest in general.
But it surprises me somewhat, given that clang for x86-64 on Linux uses 64-bit for int_fast16_t.
But to be clear, the size of the "fast" types depends on the target and
the implementation. They are not normally used for external ABI's, and
are purely internal to the generated code. Obviously you must pick a
"fast" size that is at least as big as the range you need.
On 12/09/2024 00:47, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:02, Ben Bacarisse wrote:Yes, that incorrect explanation.
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is?That seemed like the obvious explanation for the incorrect information >>>> you gave. Did you post it /knowing/ what other kinds of things are
lvalues in C just to confuse people?
Which incorrect explanation was that?
I merely said that LHSs of assigments fall into these categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I dispute that. What I said is very broadly correct. But in this newgroup
you do like to nitpick.
So to you, it is of the greatest importance that somebody doesn't just know about those four categories that they will be reading and writing all the time in C code, but also know about:
(int){A} = Y;
m which /is/ important and was also missing.
which they will encounter approximatey never. And it is also vital they
they consider:
(A) = (Y);
a distinct category.
There might be a case for this for '(Z, A) = Y;' but
that isn't allowed anyway. So it only applies to superfluous
parentheses.
Clearly I mean VALID LHSs, otherwise they wouldn't be LHSs of assignments! >>>Along with (a) _Generic expressions (where the selected arm is an
I've since learnt about a couple of other possible categories; one is with >>> compound literals like '(int){42} = 0'.
lvalue)
The _Generic forms reduce down one of those four.
It is more like a macro,
and if you're going to start with macros, there are unlimited categories
that can be created. If this is merely about syntax, then why not?
(I'd also like to see an actual usecase for _Generic on the LHS of an assignment. Perhaps one where there is a matching (symmetric?) _Generic on the RHS?)
and (b) expressions of the form X->m.
Are there any circumstances where X->m does something different from
(*X).m?
(I don't count (A), ((A)) etc as aDon't give me "come on!". I was counting forms in the same way that you
separate category; come on!)
were when I said I could think of three more. I was not counting
parentheses.
Keith mentioned this form.
The other is 'X.m' but when .m is a bitfield;What makes X.m = Y, where m is a bitfield, an extra category? It fits
the X.m = Y pattern perfectly well.
although this has the sameYour categories were syntactic. You were describing forms.
same syntax as above, internally it's somewhat different.
Not entirely. There is behaviour associated with them.
Most LHS terms can have & applied in an rvalue context for example;
bitfield accesses can't. So it's something a user of the language needs to know about.
And internally, my ASTs (where bitfields are supported) use a different
node type when X.m is a bitfield rather than a regular access.
On 12/09/2024 00:32, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:22, Ben Bacarisse wrote:And you've stated that there are differences but of course you haven't
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>>>> language implementations. C is little different other than it has aI don't want to bash you about the head, but what C says about
700-page standard that suggests a recommended model of how it's supposed to
work.
You can't really use that to bash me about the head with and maintain that
all my ideas about language implementation are wrong because C views >>>>> assignment in its own idiosyncratic manner.
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You >>>> /know/ the LH and RH side of a C assignment have different constraints >>>> (you have said so yourself) yet you persist in defending your original >>>> claim that what is needed on the two sides "is exactly the same".
I've listed the aspects that I said are the same.
listed them (as far as I can tell).
That is, if something is a legal LHS term, then its syntax, and its type, >>> are identical to that term appearing on the RHS.And you have /not/ stated, though you know it perfectly well, that the
reverse does /not/ apply -- that many "legal" RHS expressions can't
appear, legally, on the LHS.
Clearly all RHSs can't appear on the left; this is OK:
A = A + A + A + A;
but not:
A + A + A + A = A;
As for differences, there is my AST for A = B:
i32-- 1 assign:
i32-- - 1 name: a
i32-- - 2 name: b
Same node type, same type. What are the differences?
Or people simply can't grasp what I'm saying. I've given a millionBut your mistake is not that there are not millions of identical
examples of identical LHSs and RHSs,
(looking) LH sides and RH sides. No one disputes that. But in reply to
my statement that what is /required/ on both sides is not the same, you
said "I would argue that it is exactly the same".
I think you're still not getting it. In C you write:
A = B
to assign simple value types. Or you write B = A the other way around.
You write A as A, B as B no matter which side it is.
In BLISS which you claim to be more symmetric, you have to write:
A = .B
for the same operation (copy B's value into A), or B = .A for the other way around.
On 10/09/2024 01:58, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 16:36, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 08/09/2024 23:34, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:There are different kinds of cache here. Some of the Cortex-M cores >>>>> have optional caches (i.e., the microcontroller manufacturer can choose >>>>> to have them or not).
IIUC STM4 series has cache, and some of them are not so big. There >>>>>> are now several chinese variants of STM32F103 and some of them have >>>>>> caches (some very small like 32 words, IIRC one has 8 words and it >>>>>> is hard to decide if this very small cache or big prefetch buffer). >>>>>
And while microcontrollers sometimes have a limited form of branch >>>>>>> prediction (such as prefetching the target from cache), the more >>>>>>> numerous and smaller devices don't even have instruction caches. >>>>>>> Certainly none of them have register renaming or speculative execution. >>>>>>
<https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization>
I do not see relevent information at that link.
There is a table of the Cortex-M cores, with the sizes of the optional
caches.
Yes.
Flash memory, flash controller peripherals, external memory interfaces >>>>> (including things like QSPI) are all specific to the manufacturer,
rather than part of the Cortex M cores from ARM. Manufacturers can do >>>>> whatever they want there.
AFAIK typical Cortex-M design has core connected to "bus matrix".
It is up to chip vendor to decide what else is connected to bus matrix. >>>
However, there are other things connected before these crossbar
switches, such as tightly-coupled memory (if any).
TCM is _not_ a cache.
Correct. (I did not suggest or imply that it was.)
And the cpu caches
(if any) are on the cpu side of the switches.
Caches are attached were system designer thinks they are useful
(and possible). Word "cache" has well-estabished meaning and
ARM (or you) has no right to redefine it.
I am using it in the manner ARM uses it when talking about ARM
processors and microcontroller cores. I think that is the most relevant
way to use the term here. The term "cache" has many meanings in many contexts - there is no single precise "well-established" or "official" meaning.
Context is everything. That is why I have been using the term
"cpu cache" for the cache tied tightly to the cpu itself, which comes as
part of the core that ARM designs and delivers, along with parts such as
the NVIC.
And I have tried to use terms such as "buffer" or "flash
controller cache" for the memory buffers often provided as part of flash controllers and memory interfaces on microcontrollers, because those are terms used by the microcontroller manufacturers.
Manufacturers also have a
certain amount of freedom of the TCMs and caches, depending on which
core they are using and which licenses they have.
There is a convenient diagram here:
<https://www.electronicdesign.com/technologies/embedded/digital-ics/processors/microcontrollers/article/21800516/cortex-m7-contains-configurable-tightly-coupled-memory>
For me it does not matter if it is ARM design or vendor specific.
Normal internal RAM is accessed via bus matrix, and in MCU-s that
I know about is fast enough so that cache is not needed. So caches
come into play only for flash (and possibly external memory, but
design with external memory probably will be rather large).
Typically you see data caches on faster Cortex-M4 microcontrollers with
external DRAM, and it is also standard on Cortex-M7 devices. For the
faster chips, internal SRAM on the AXI bus is not fast enough. For
example, the NXP i.mx RT106x family typically run at 528 MHz core clock, >>> but the AXI bus and cross-switch are at 133 MHz (a quarter of the
speed). The tightly-coupled memories and the caches run at full core speed.
OK, if you run core at faster clock than the bus matrix, then cache
attached on core side make a lot of sense. And since cache has to
compensate for lower bus speed it must be resonably large.
Yes.
But
if you look at devices where bus matrix runs at the same clock
as the core, then it makes sense to put cache on the other side.
No.
You put caches as close as possible to the prime user of the cache. If
the prime user is the cpu and you want to cache data from flash,
external memory, and other sources, you put the cache tight up against
the cpu - then you can have dedicated, wide, fast buses to the cpu.
But it can also make sense to put small buffers as part of memory
interface controllers. These are not organized like data or instruction caches, but are specific for the type of memory and the characteristics
of it.
How this is done depends on details of the interface, details of
the internal buses, and how the manufacturer wants to implement it. For example, on one microcontroller I am using there are queues to let it
accept multiple flash read/write commands from the AHB bus and the IPS
bus, but read-ahead is controlled by the burst length of read requests
from the cross-switch (which in turn will come from cache line fill
requests from the cpu caches). On a different microcontroller, the read-ahead logic is in the flash controller itself as that chip has a
simpler internal bus where all read requests will be for 32 bits (it has
no cpu caches). An external DRAM controller, on the other hand, will
have queues and buffers optimised for multiple smaller transactions and
be able to hold writes in queues that get lower priority than read requests.
These sorts of queues and buffers are not generally referred to as
"caches", because they are specialised queues and buffers. Sometimes
you might have something that is in effect perhaps a two-way
single-entry 16 byte wide read-only cache, but using the term "cache"
here is often confusing. At best it is a "flash controller cache", and
very distinct from a "cpu cache".
It seems that vendor do not like to say that they use cache, instead
that use misleading terms like "flash accelerator".
That all depends on the vendor, and on how the flash interface
controller. Vendors do like to use terms that sound good, of course!
So a "cache" of 32 words is going to be part of the flash interface, not >>>>> a cpu cache
Well, caches never were part of CPU proper, they were part of
memory interface. They could act for whole memory or only for part
that need it (like flash). So I do not understand what "not a cpu
cache" is supposed to mean. More relevant is if such thing act
as a cache, 32 word things almost surely will act as a cache,
8 word thing may be a simple FIFO buffer (or may act smarter
showing behaviour typical of caches).
Look at the diagram in the link I gave above, as an example. CPU caches >>> are part of the block provided by ARM and are tightly connected to the
processor. Control of the caches (such as for enabling them) is done by >>> hardware registers provided by ARM, alongside the NVIC interrupt
controller, SysTick, MPU, and other units (depending on the exact
Cortex-M model).
This is completely different from the small buffers that are often
included in flash controllers or external memory interfaces as
read-ahead buffers or write queues (for RAM), which are as external the
processor core as SPI, UART, PWM, ADC, and other common blocks provided
by the microcontroller manufacturer.
The disscussion started about possible interaction of caches
and virtual function dispatch.
OK - I admit to having lost track of the earlier discussion, so that is helpful.
This interaction does not depend
on you calling it cache. It depends on cache hits/misses,
their cost and possible eviction. And actually small caches
can give "interesting" behaviour: with small code footprint there
may be 100% hit ratio, but one extra memory reference may lead
to significant misses. And even small caches behave differently
then simple buffers.
I agree that behaviour can vary significantly.
When you have a "flash controller cache" - or read-ahead buffers - you typically have something like a 60-80% hit ratio for sequential code and nearly 100% for very short loops (like you'd have for a memcpy() loop).
You have close to 0% hit ratio for branches or calls, regardless of
whether they are virtual or not (with virtual function dispatch
generally having one extra indirection at 0% hit rate). This is the
kind of "cache" you often see in microcontrollers with internal flash
and clock speeds of up to perhaps 150 Mz, where the flash might be at a quarter of the main cpu clock.
(which are typically 16KB - 64KB,
I wonder where you found this figure. Such size is typical for
systems bigger than MCU-s. It could be useful for MCU-s with
flash a on separate die, but with flash on the same die as CPU
much smaller cache is adequate.
Look at the Wikipedia link I gave. Those are common sizes for the
Cortex-M7 (which is pretty high-end), and for the newer generation of
Cortex-M35 and Cortex-M5x parts. I have on my desk an RTO1062 with a
600 MHz Cortex-M7, 1 MB internal SRAM, 32 KB I and D caches, and
external QSPI flash.
OK, as I wrote it makes sense for them. But for smaller machines
much smaller caches may be adequate.
As I have said, they are not really caches in the same sense as you have
for a cpu cache.
But certainly a "flash controller cache" or read-ahead
buffer (especially if there are two of them) can make a big difference
to the throughput of a microcontroller, and equally certainly a cpu
cache would be an unreasonable cost in die area, power, and licensing
fees for most microcontrollers. Thus these small buffers - or very
small, very specialised caches in the flash controller - are a good idea.
and only found on bigger
microcontrollers with speeds of perhaps 120 MHz or above). And yes, it >>>>> is often fair to call these flash caches "prefetch buffers" or
read-ahead buffers.
Typical code has enough branches that simple read-ahead beyond 8
words is unlikely to give good results. OTOH delivering things
that were accessed in the past and still present in the cache
gives good results even with very small caches.
There are no processors with caches smaller than perhaps 4 KB - it is
simply not worth it.
Historicaly there were processors with small caches. 256B in
Motorla chips and I think smaller too. It depends on the whole
design.
For a general cpu data cache on a modern cpu, the cache control logic is probably going to require the same die area as a few KB of cache
storage, as a minimum - so it makes no sense to have such small cpu
caches. The logic for instruction caches is simpler. In days gone by, balances were different and smaller caches could be useful. The 68020
had a 256 byte instruction cache, and the 68030 and 68040 added a 256
byte data cache. Both were single way.
Currently for "big" processors really small caches seem
to make no sense. Microconrollers have their own constaints.
Manufacturer may decide that cache giving 10% average improvement
is not worth uncertainilty of execution time. Or may decide that
small cache is the cheapest way to get better benchmark figures.
You are correct that microcontrollers have different constraints, and
that jitter and variation of timing is far more of a cost in
microcontrollers than it is on "big" processors, where throughput is
key. The other factor here is latency. On earlier designs such as the aforementioned M68k family, you could often add a fair bit of logic
without requiring extra clock cycles. Thus the cache was "free". That
is different now, even on microcontrollers. Adding a cpu cache on even
the slowest of modern microcontrollers will mean at least a clock cycle
extra on cache misses compared to no cache - for medium devices (say,
120 MHz Cortex-M4) it would mean 2 or 3 extra cycles. So unless you are getting a significant hit ratio, it is not worth it.
Putting read-ahead buffers and a "micro-cache", if that term suits you,
at the flash controller and other memory interfaces is, however, free in terms of clock cycles and latency - these parts run at a higher clock
rate than the flash itself.
Read-ahead buffers on flash accesses are helpful,
however, because most code is sequential most of the time. It is common >>> for such buffers to be two-way, and to have between 16 and 64 bytes per
way.
If you read carefully description of STM "flash accelerator" it is
clear that this is classic cache, with line size matched to flash,
something like 2-set associativity, conflicts and eviction.
Historically there were variations, some caches only cache targets
of jumps and use prefetch buffer for linear code. Such caches
can be effective at very small size.
I don't know the STM "flash accelerator" specifically - there are many
ARM microcontrollers and I have not used them all. But while it is true
that some of these are organised in a similar way to extremely small and restricted caches, I think using the word "cache" alone here is
misleading. That's why I have tried to distinguish and qualify the term.
And in the context of virtual function dispatch, a two-way single line micro-cache is pretty much guaranteed to have a cache miss when doing
such indirect calls as you need the current code, the virtual method
table, and the virtual method itself to be in cache simultaneously to
avoid a miss. But these flash accelerators still make a big difference
to the speed of code in general.
Bart <bc@freeuk.com> writes:
On 12/09/2024 00:47, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:02, Ben Bacarisse wrote:Yes, that incorrect explanation.
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is? >>>>> That seemed like the obvious explanation for the incorrect information >>>>> you gave. Did you post it /knowing/ what other kinds of things arelvalues in C just to confuse people?
Which incorrect explanation was that?
I merely said that LHSs of assigments fall into these categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I dispute that. What I said is very broadly correct. But in this newgroup
you do like to nitpick.
Someone who wants to write p->m = 42 would not consider it nitpicking if
your compiler did not accept that form of LHS.
But I agree I was simply correcting a small error.
Is there no part of C you can't misrepresent?
Yes, that incorrect explanation.
I merely said that LHSs of assigments fall into these categories:Yes, that incorrect explanation.
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I dispute that. What I said is very broadly correct. But in this newgroup
you do like to nitpick.
Someone who wants to write p->m = 42 would not consider it nitpicking if
your compiler did not accept that form of LHS.
But I agree I was simply correcting a small error. Why did you not just
say "yes, I forgot a few cases"?
So to you, it is of the greatest importance that somebody doesn't just know >> about those four categories that they will be reading and writing all the
time in C code, but also know about:
(int){A} = Y;
You see why I wonder if you had a political career? This is pure spin.
There is no technical argument here, just an attempt to mock someone
pointing out a truth. I never even suggested that it was important,
just that it was a missing case. And, still spinning away, you ignore
m which /is/ important and was also missing.
Bart <bc@freeuk.com> writes:
On 12/09/2024 20:38, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]
It's not that complicated, not with C anyway.
Agreed. Let's stop doing that. (Your specific statement that there are *four* categories triggered a lot of the "too much".)
Upthread, you wrote:
That's the only thing that needs to 'change', which I don't think is
onerous anyway.
Would you like to clarify what you think needs to change?
"To be more precise, gcc parser when seeing a variablecreates read reference to this variable. When parser realizes that
On 13/09/2024 04:16, Waldek Hebisch wrote:
You put caches as close as possible to the prime user of the
cache. If the prime user is the cpu and you want to cache data
from flash, external memory, and other sources, you put the cache
tight up against the cpu - then you can have dedicated, wide, fast
buses to the cpu.
I would say that there is a tradeoff between cost and effect. And
there is question of technical possibility. For example, 386 was
sold as a chip, and all that a system designer could do was to put
a cache ont the motherboard. On chip cache would be better, but was
not possible.
There can certainly be such trade-offs. I don't remember the details
of the 386, but I /think/ the cache was connected separately on a
dedicated bus, rather than on the bus that went to the memory
controller (which was also off-chip, on the chipset).
So it was
logically close to the cpu even though it was physically on a
different chip. I think if these sorts of details are of interest, a
thread in comp.arch might make more sense than comp.lang.c.
David Brown <david.brown@hesbynett.no> wrote:
On 12/09/2024 01:59, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On many cpus, using sizes smaller than the full register size means
doing sign extensions or masking operations at various times - thus full >>>> size register operations can often be more efficient. On such systems >>>> you will find that int_fast16_t is 32-bit or 64-bit, according to the
register width. On other cpus, some common ALU operations on full-size >>>> operands can be slower than for smaller operands (such as on the 68000). >>>> There, int_fast16_t will be 16-bit.
Compiler authors know what will usually be faster on the target. There >>>> will always be some exceptions (division is usually faster on smaller
operands, for example). But if you don't know the target - as is the
case of portable code - the compiler will usually make a better choice >>>> here than you would.
BTW, I just played with Clang 18 on 64-bit FreeBSD. It has 32-bit
int_fast16_t. gcc in Linux makes it 64-bit. Who is right?
Technically, both are right - implementations can use any integer type
of at least 16 bits here, whatever they think is fastest in general.
But it surprises me somewhat, given that clang for x86-64 on Linux uses
64-bit for int_fast16_t.
Well, both satisfy "hard" requirements. But the question was which
type is faster.
But to be clear, the size of the "fast" types depends on the target and
the implementation. They are not normally used for external ABI's, and
are purely internal to the generated code. Obviously you must pick a
"fast" size that is at least as big as the range you need.
I think that Linux (and probably FreeBSD too) considers size of
fast type as part of ABI (regardless of gudelines those types
certainly leaked into "public" interfaces). Such ABI change is
probably viewed as not worth doing.
And concering choice on x86_64, AFAIK for operations on numbers of
the same size 32-bit gives fastest operations. 16-bit had two
disadvantages, big one due to partial register stalls, small one
due to larger size (operand size prefix). 64-bit requires bigger
code (more need to use prefixes) and bigger data.
When mixing
types, 32-bit numbers are automatically zero extended, so there
is no extra cost when mixing unsigend numbers.
So what remains
is mixing signed 32-bit integers with 64-bit ones. Addresses
use 64-bit artitmetic, so that requires sign extention. OTOH
in arithmetic "fast" types are likely to be mixed with exact
32-bit types and then making "fast" types 32-bit is faster
overall. So, there are bets/assumptions which usage is more
frequent. OTOH, choice between 32-bit and 64-bit fast types
is unlikely to make _much_ difference.
David Brown <david.brown@hesbynett.no> wrote:
On 10/09/2024 01:58, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 09/09/2024 16:36, Waldek Hebisch wrote:
Context is everything. That is why I have been using the term
"cpu cache" for the cache tied tightly to the cpu itself, which comes as
part of the core that ARM designs and delivers, along with parts such as
the NVIC.
Logically, given that there was "tightly attached memory", this should
be called "tightly attached cache" :)
Logicallt "cpu cache" is cache sitting on path between the CPU and a memory device. It does not need to be tightly attached to the CPU, L2 and L3
caches in PC-s are not "tightly attached".
And I have tried to use terms such as "buffer" or "flash
controller cache" for the memory buffers often provided as part of flash
controllers and memory interfaces on microcontrollers, because those are
terms used by the microcontroller manufacturers.
"flash cache" looks resonable. Concerning difference between a buffer
and a cache there is indeed some fuzzines here. AFAICS word "buffer"
is used for logically very simple devices, once operation becomes a bit
more interesting it is usually called a cache. Anyway, given fuzzines
saying that something called a buffer is not a cache is risky, it
may have all features associalted normally with caches, and in such
a case deserves to be called a cache.
But
if you look at devices where bus matrix runs at the same clock
as the core, then it makes sense to put cache on the other side.
No.
You put caches as close as possible to the prime user of the cache. If
the prime user is the cpu and you want to cache data from flash,
external memory, and other sources, you put the cache tight up against
the cpu - then you can have dedicated, wide, fast buses to the cpu.
I would say that there is a tradeoff between cost and effect. And
there is question of technical possibility. For example, 386 was
sold as a chip, and all that a system designer could do was to put
a cache ont the motherboard. On chip cache would be better, but was
not possible.
IIUC in case of Cortex-M0 or say M4 manufactures get
ARM core with busses intended to be connected to the bus matrix.
Manufacturs could add extra bus matrix or crossbar just to access cache,
but bus width is specified by ARM design.
If main bus matrix and RAM
is clocked at CPU freqency the extra bus matrix and cache would
only add extra latency for no gain (of course, this changes when
main bus matrix runs at lower clock).
So putting cache only
at flash interface makes sense: it helps there and on lower end
chips is not needed elswere.
Also, concerning caches in MCU-s note
that for writable memory there is problem of cache coherency. In
particular several small MCU-s have DMA channels. Non-coherent design
would violate user expectations and would be hard to use.
OTOH putting
coherent cache on memory side means extra complication to bus matrix (I
do not know what ARM did with their bigger cores). Flash being
mainly read-only does not have this problem.
But it can also make sense to put small buffers as part of memory
interface controllers. These are not organized like data or instruction
caches, but are specific for the type of memory and the characteristics
of it.
Point is that in many cases they are organized like classic caches.
They cover only flash, but how it is different from caches in PC-s
that covered only part of possible RAM?
How this is done depends on details of the interface, details of
the internal buses, and how the manufacturer wants to implement it. For
example, on one microcontroller I am using there are queues to let it
accept multiple flash read/write commands from the AHB bus and the IPS
bus, but read-ahead is controlled by the burst length of read requests
from the cross-switch (which in turn will come from cache line fill
requests from the cpu caches). On a different microcontroller, the
read-ahead logic is in the flash controller itself as that chip has a
simpler internal bus where all read requests will be for 32 bits (it has
no cpu caches). An external DRAM controller, on the other hand, will
have queues and buffers optimised for multiple smaller transactions and
be able to hold writes in queues that get lower priority than read requests. >>
These sorts of queues and buffers are not generally referred to as
"caches", because they are specialised queues and buffers. Sometimes
you might have something that is in effect perhaps a two-way
single-entry 16 byte wide read-only cache, but using the term "cache"
here is often confusing. At best it is a "flash controller cache", and
very distinct from a "cpu cache".
From STM32F400 reference manual:
: Instruction cache memory
:
: To limit the time lost due to jumps, it is possible to retain 64 lines
: of 128 bits in an instruction cache memory.
That is 1kB instruction cache. In most of their marketing material they
say "flash accelerator", but in reference manual admited that this is a
cache (OK, they have also prefetch buffer and possibly "flash accelerator
= cache + buffer").
Simlarly, documentation of RP2040 says:
: An internal cache remembers the contents of recently-accessed flash
: locations, which accelerates the average bandwidth and latency of
: the interface.
Granted, RP2040 is rather big chip, but the same thing is used in smaller ones.
I agree that behaviour can vary significantly.
When you have a "flash controller cache" - or read-ahead buffers - you
typically have something like a 60-80% hit ratio for sequential code and
nearly 100% for very short loops (like you'd have for a memcpy() loop).
You have close to 0% hit ratio for branches or calls, regardless of
whether they are virtual or not (with virtual function dispatch
generally having one extra indirection at 0% hit rate). This is the
kind of "cache" you often see in microcontrollers with internal flash
and clock speeds of up to perhaps 150 Mz, where the flash might be at a
quarter of the main cpu clock.
Well, with 64 lines and 2-set associativlity STM cache can give you
quite decent hit ratio on branchy code, as long as working set is
not too large. I does not need to be a simple loop. Already 3
lines can be enough if you have single call to a simple function
and call is in the loop (and if you call via function pointer
compiler can not inline the function). More realistically, 8 lines
will cover several cases where code jumps between small number
of locations.
On 13/09/2024 04:16, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
I don't disagree that other types of buffer can fall under a generic
concept of "cache". And in some cases they may even have the same
logical build-up as a tiny and limited version of the cpu caches. But I >don't think it helps to use exactly the same terms for things that are
in significantly different places on the chip, with very different
balances in their designs, and for different effects in their detailed >working.
It is fair enough to talk about a "flash cache" for buffers that are
designed somewhat like a cache, with at least two entries indexed by an >address hash (usually just some of the lower address bits), and with
tags including the rest of the address bits. It is misleading for
systems where you just have a read-ahead buffer or two, or a queue
system. Unlike the cpu caches, the architecture of such flash
accelerators varies wildly for different manufacturers and their
different microcontroller models.
On 13/09/2024 00:46, Ben Bacarisse wrote:
Bart <bc@freeuk.com> writes:
On 12/09/2024 00:47, Ben Bacarisse wrote:Someone who wants to write p->m = 42 would not consider it nitpicking if
Bart <bc@freeuk.com> writes:
On 11/09/2024 01:02, Ben Bacarisse wrote:Yes, that incorrect explanation.
Bart <bc@freeuk.com> writes:
Sorry, did your remark above suggest I don't know what an lvalue is? >>>>>> That seemed like the obvious explanation for the incorrect information >>>>>> you gave. Did you post it /knowing/ what other kinds of things are >>>>>> lvalues in C just to confuse people?
Which incorrect explanation was that?
I merely said that LHSs of assigments fall into these categories:
A = Y; // name
*X = Y; // pointer
X[i] = Y; // index
X.m = Y; // member select
I dispute that. What I said is very broadly correct. But in this newgroup >>> you do like to nitpick.
your compiler did not accept that form of LHS.
But I agree I was simply correcting a small error.
It didn't seem like it. You keep saying things like this:
Is there no part of C you can't misrepresent?
And (see above):
Yes, that incorrect explanation.
Suggesting it was completely wrong rather than there being some corner
cases that might have been included too.
You'd have a point if I was writing some treatise on C and this was
supposed to be a comprehensive list of all lvalue terms.
The context was that I was pointing out the simpler kinds of LHSs that C allows, compared with other languages where you could do stuff like this:
IF c THEN a ELSE b FI := x
which was exactly what was being queried.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
Assuming all that is right, I recommend
typedef __uint128_t U128;
typedef __int128_t S128;
which works in both gcc and clang (I don't know yet about
Visual Studio).
The documented names are `__int128` and `unsigned __int128`.
Both gcc and clang do recognize `__int128_t` and `__uint128_t`,
but I wouldn't recommend relying on an undocumented feature.
__int128 is treated as a keyword.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
Have you tried looking at other uses of the word "may" in the C
standard to see if that sheds some light on the question?
If you have any actual insight to offer, feel free to do so.
Is there some reason you mind my asking a civil question?
In my view there is no question about the intent here. If
I'm going to try to help you resolve your uncertainty, it
would be helpful to have a better understanding of your
reasoning process. That's why I asked the question.
I got the impression that you were providing vague hints and
deliberately hiding information. As you know, several other people
here have gotten the same impression in similar circumstances.
Perhaps that wasn't your intent, but in my opinion it would be to
your benefit to be more aware of how you come across.
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >> language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to >> work.
You can't really use that to bash me about the head with and maintain that >> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
I don't want to bash you about the head, but what C says about
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You /know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same". You
must, surely, be arguing simply for the fun of it.
Tim suggests that there is communication failure here -- that you have
not expressed what you mean clearly enough. That may be so, but I can't
see how to interpret what you've written in any other way.
Ben Bacarisse <ben@bsb.me.uk> writes:
Bart <bc@freeuk.com> writes:
And yes I'm still committed to that symmetry. I'ved used it for countless >>> language implementations. C is little different other than it has a
700-page standard that suggests a recommended model of how it's supposed to >>> work.
You can't really use that to bash me about the head with and maintain that >>> all my ideas about language implementation are wrong because C views
assignment in its own idiosyncratic manner.
I don't want to bash you about the head, but what C says about
assignment has /always/ been the point, and your implementation of C
will be wrong if you don't follow the rules about C's assignments. You
/know/ the LH and RH side of a C assignment have different constraints
(you have said so yourself) yet you persist in defending your original
claim that what is needed on the two sides "is exactly the same". You
must, surely, be arguing simply for the fun of it.
Tim suggests that there is communication failure here -- that you have
not expressed what you mean clearly enough. That may be so, but I can't
see how to interpret what you've written in any other way.
I'm coming around to the point of view that Bart isn't really
interested in communicating. He seems not to listen to what
other people say, and either he can't be bothered to say what
he really means or he says things in a personal idiosyncratic
vernacular that no one else understands. I'm okay with people
who are making a sincere effort to communicate and are just
having trouble doing so. With Bart though more and more the
impression I get is that he isn't really trying because at
some level he doesn't care if he communicates or not.
Trying to communicate in this Standards-obsessed newsgroup is like
trying to have a meaningful discussion with Bible-bashers.
On 2024-09-16, Bart <bc@freeuk.com> wrote:
Trying to communicate in this Standards-obsessed newsgroup is like
trying to have a meaningful discussion with Bible-bashers.
Yes; "I hsve my own personal version of the Bible in which many of its arbitrary stories are otherwise" is probably not a good way to approach
a Bible study group.
On 16/09/2024 12:30, Kaz Kylheku wrote:
On 2024-09-16, Bart <bc@freeuk.com> wrote:
Trying to communicate in this Standards-obsessed newsgroup is like
trying to have a meaningful discussion with Bible-bashers.
Yes; "I hsve my own personal version of the Bible in which many of
its arbitrary stories are otherwise" is probably not a good way to
approach a Bible study group.
So this is a Bible study group now?
I suspected as much.
I suppose the diverse requirements of everyday life, and individuals' different personalities, responsibilities and attitudes all have to
be left outside the door.
On 16/09/2024 12:30, Kaz Kylheku wrote:
On 2024-09-16, Bart <bc@freeuk.com> wrote:
Trying to communicate in this Standards-obsessed newsgroup is like
trying to have a meaningful discussion with Bible-bashers.
Yes; "I hsve my own personal version of the Bible in which many of its
arbitrary stories are otherwise" is probably not a good way to approach
a Bible study group.
So this is a Bible study group now?
I suspected as much.
I suppose the diverse requirements of everyday life, and individuals' different personalities, responsibilities and attitudes all have to be
left outside the door.
On 16/09/2024 12:30, Kaz Kylheku wrote:...
Yes; "I hsve my own personal version of the Bible in which many of its
arbitrary stories are otherwise" is probably not a good way to approach
a Bible study group.
So this is a Bible study group now?
On 2024-09-16, Bart <bc@freeuk.com> wrote:
On 16/09/2024 12:30, Kaz Kylheku wrote:...
Yes; "I hsve my own personal version of the Bible in which many of its
arbitrary stories are otherwise" is probably not a good way to approach
a Bible study group.
So this is a Bible study group now?
No, but there are some key similarities. Both bible study groups and
this newsgroup have an authoritative text to reference. However, the
nature of that authority is quite different in the two cases. Bible
study groups believe that the Bible is divinely inspired. Those who are sufficiently familiar with the C standard know that it was created by a committee of experts, fully capable of making mistakes. Many (most?) Believers consider the Bible to be incapable of being wrong.
The C standard is also incapable of being wrong, but in a very different sense - the C standard defines C, there is no alternative to compare it
with, in order to say that the C standard is wrong. The C standard might
be inconsistent, unimplementable, badly designed, or incomprehensible,
among many other defects if might have - but as the official definition
of C, it cannot be wrong.
Any such defects can be corrected by filing a defect report and
convincing the committee that the report is correct. If they agree, the
next version of the standard is likely to contain revised wording to
address the issue. Try doing that with the Bible.
On 2024-09-16, Bart <bc@freeuk.com> wrote:
On 16/09/2024 12:30, Kaz Kylheku wrote:...
Yes; "I hsve my own personal version of the Bible in which many of its
arbitrary stories are otherwise" is probably not a good way to approach
a Bible study group.
So this is a Bible study group now?
No, but there are some key similarities. Both bible study groups and
this newsgroup have an authoritative text to reference. However, the
nature of that authority is quite different in the two cases. Bible
study groups believe that the Bible is divinely inspired. Those who are sufficiently familiar with the C standard know that it was created by a committee of experts, fully capable of making mistakes. Many (most?) Believers consider the Bible to be incapable of being wrong.
[...]
Somewhat trickier are the cases where some other document says
"... the standard says X, but that doesn't make any sense. It's
clear that they actually meant Y, and you can just write your code accordingly." Tim Rentsch is a prolific source of such comments.
On 2024-09-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 08.09.2024 16:12, James Kuyper wrote:
On 9/8/24 00:39, Janis Papanagnou wrote:
...
That's why I immediately see the necessity that compiler creators
need to know them in detail to _implement_ "C". And that's why I
cannot see how the statement of the C-standard's "most important
purpose" would
sound reasonable (to me). ...
I agree - the most important purpose is for implementors, not
developers.
... I mean, what will a programmer get from the "C" standard that
a well written text book doesn't provide?
What the C standard says is more precise and more complete than
what most textbooks say.
Exactly. And this precision is what makes standard often difficult
to read (for programming purposes for "ordinary" folks).
The C grammar is not presented in a nice way in ISO C.
It uses nonsensical categories. For instance a basic expression
like A is considered a unary-expression. A newcomer looking at the
grammar for assignment will be wondering how on Earth the A in A = B
is a unary expression, when it contains no unary operator.
The unary-expression is not given in the immediately preceding
section, and no section references are given; you have to go
searching through the document to find it.
I also suspect programmers not versed in parsing and grammars will
not intuit that assignment associates right to left. Someone who
remembers their compiler course from university will know that the
right hand side
"unary-expression assignment-operator assignment-expression"
has the assignment-expression on the right, and is therefore
identified as right-recursive, and that leads to right association.
I strongly suspect that the vast majority of the C coders on the
planet (as well as users of other languages that have operator
grammars) refer to operator precedence tables that fit on one
page, rather than flipping around in a telescopic grammar that
goes on for pages.
On 16.09.2024 18:19, James Kuyper wrote:
On 2024-09-16, Bart <bc@freeuk.com> wrote:
On 16/09/2024 12:30, Kaz Kylheku wrote:...
Yes; "I hsve my own personal version of the Bible in which many of its >>>> arbitrary stories are otherwise" is probably not a good way to approach >>>> a Bible study group.
So this is a Bible study group now?
No, but there are some key similarities. Both bible study groups and
this newsgroup have an authoritative text to reference. However, the
nature of that authority is quite different in the two cases. Bible
study groups believe that the Bible is divinely inspired. Those who are
sufficiently familiar with the C standard know that it was created by a
committee of experts, fully capable of making mistakes. Many (most?)
Believers consider the Bible to be incapable of being wrong.
But wasn't the Bible created in a ("Chinese whispers"?) way like
([God] ->) human -> ... -> human -> Bible write-down
with (a lot?) of ([possibly] errant) humans in between?
Disclaimer: I don't know how many instances of "human" were involved.
On 01.09.2024 22:07, Tim Rentsch wrote:
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Is that part of a preamble or rationale given in the C standard?
That target audience would surely surprise me. Myself I've
programmed in quite some programming languages and never read a
standard document of the respective language, nor did I yet met
any programmer who have done so. All programmer folks I know used
text books to learn and look up things and specific documentation
that comes with the compiler or interpreter products. (This is of
course just a personal experience.)
I've also worked a lot with standards documents in various areas
(mainly ISO and ITU-T standards but also some others). [..]
That's why I immediately see the necessity that compiler creators
need to know them in detail to _implement_ "C". And that's why I
cannot see how the statement of the C-standard's "most important
purpose" would sound reasonable (to me).
I mean, what will a programmer get from the "C" standard that a
well written text book doesn't provide?
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 01.09.2024 22:07, Tim Rentsch wrote:
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Is that part of a preamble or rationale given in the C standard?
That target audience would surely surprise me. Myself I've
programmed in quite some programming languages and never read a
standard document of the respective language, nor did I yet met
any programmer who have done so. All programmer folks I know used
text books to learn and look up things and specific documentation
that comes with the compiler or interpreter products. (This is of
course just a personal experience.)
I've also worked a lot with standards documents in various areas
(mainly ISO and ITU-T standards but also some others). [..]
My comment is only about the C standard, not any other standards
documents.
[...]
I mean, what will a programmer get from the "C" standard that a
well written text book doesn't provide?
The text books being imagined here don't exist, because there is no
market for them.
Very few developers read the C standard.
But the
impact and influence of those who do is much larger than the small
numbers would suggest.
On 16/09/2024 18:19, James Kuyper wrote:...
The C standard is also incapable of being wrong, but in a very different
sense - the C standard defines C, there is no alternative to compare it
with, in order to say that the C standard is wrong. The C standard might
be inconsistent, unimplementable, badly designed, or incomprehensible,
among many other defects if might have - but as the official definition
of C, it cannot be wrong.
Any such defects can be corrected by filing a defect report and
convincing the committee that the report is correct. If they agree, the
next version of the standard is likely to contain revised wording to
address the issue. Try doing that with the Bible.
At the risk of offending people, I'd say this /has/ been done with the
Bible countless times. There are dozens of major versions of the Bible
with different selections of books and sections of the books. There are hundreds of translations for each version, even counting just
translations into English, based on different source texts and very
different styles of translation. And that's before you get to major re-writes, like Mormonism (though perhaps that's more akin to moving
from C to Rust).
Unlike C, it is not a nice linear progression with each new version superseding the previous versions. But we still do see some "C90
fanatics" that are as convinced in their viewpoint as some King James fans!
On 16/09/2024 18:19, James Kuyper wrote:
Any such defects can be corrected by filing a defect report and
convincing the committee that the report is correct. If they agree, the
next version of the standard is likely to contain revised wording to
address the issue. Try doing that with the Bible.
At the risk of offending people, I'd say this /has/ been done with the
Bible countless times.
On 17.09.2024 15:57, Tim Rentsch wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 01.09.2024 22:07, Tim Rentsch wrote:
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Is that part of a preamble or rationale given in the C standard?
That target audience would surely surprise me. Myself I've
programmed in quite some programming languages and never read a
standard document of the respective language, nor did I yet met
any programmer who have done so. All programmer folks I know used
text books to learn and look up things and specific documentation
that comes with the compiler or interpreter products. (This is of
course just a personal experience.)
I've also worked a lot with standards documents in various areas
(mainly ISO and ITU-T standards but also some others). [..]
My comment is only about the C standard, not any other standards
documents.
Yes, that was obvious.
Are trying to say that the "C standard" is substantially different
with respect to "readability" to other standards?
- In the context
of what has been said, that it's a replacement of a textbook (or at
least maybe a supplement)?
[...]
I mean, what will a programmer get from the "C" standard that a
well written text book doesn't provide?
The text books being imagined here don't exist, because there is no
market for them.
I'm speaking about existing textbooks for programming languages.
(Not sure what you're reading or implying here.)
Very few developers read the C standard.
Yes, that was also my impression. (And I'm sure to know the reason; standards are not suited for, not written for general programmers.
they, IMO obviously, have another target group.)
But the
impact and influence of those who do is much larger than the small
numbers would suggest.
What influence?
On 2024-09-16, David Brown <david.brown@hesbynett.no> wrote:
On 16/09/2024 18:19, James Kuyper wrote:
Any such defects can be corrected by filing a defect report and
convincing the committee that the report is correct. If they agree, the
next version of the standard is likely to contain revised wording to
address the issue. Try doing that with the Bible.
At the risk of offending people, I'd say this /has/ been done with the
Bible countless times.
However, it was done 1) for ideological reasons, not to actually correct
any of the obviously ridiculous stuff and
2) while pretending that it's
all still literally God's word (and believing it).
It's like if someone took C99 and removed, say, designated initializers, because they just don't like them, and then preached that their version
of the document is still ISO 9899:1999, with people believing it and spreading the falsified document along with that claim that it is
still the word of the ISO committee.
On 9/16/24 13:13, David Brown wrote:
On 16/09/2024 18:19, James Kuyper wrote:...
The C standard is also incapable of being wrong, but in a very different >>> sense - the C standard defines C, there is no alternative to compare it
with, in order to say that the C standard is wrong. The C standard might >>> be inconsistent, unimplementable, badly designed, or incomprehensible,
among many other defects if might have - but as the official definition
of C, it cannot be wrong.
Any such defects can be corrected by filing a defect report and
convincing the committee that the report is correct. If they agree, the
next version of the standard is likely to contain revised wording to
address the issue. Try doing that with the Bible.
At the risk of offending people, I'd say this /has/ been done with the
Bible countless times. There are dozens of major versions of the Bible
with different selections of books and sections of the books. There are
hundreds of translations for each version, even counting just
translations into English, based on different source texts and very
different styles of translation. And that's before you get to major
re-writes, like Mormonism (though perhaps that's more akin to moving
from C to Rust).
There's a key difference: there's a central authority responsible for C,
the ISO C committee. Defect reports must be filed with them, and new
versions of the C standard are issued by them.
The different versions of the Bible that you refer to generally
correspond to schisms in the community of Believers, with one version of
the Bible accepted by one side of the split, and a different version by
the other side, with neither side accepting the authority of the other
to determine which version is correct.
The authority for the Bible that corresponds to the C committee for the
C standard should be God, but to an atheist like me, it's not clear that
He's playing any active public role in clarifying which version of the
Bible should be used. If He's taking any active role, it would appear to
be in the form of telling individual Believers which version they should believe, with different Believers reporting having gotten different
advice from Him on the matter.
Unlike C, it is not a nice linear progression with each new version
superseding the previous versions. But we still do see some "C90
fanatics" that are as convinced in their viewpoint as some King James fans!
The C90 fanatics do seem to be a good analogy to the Christian
schismatics. They basically don't accept the authority of ISO to change
the C standard, despite the fact that it became a standard under ISO auspices. However, they are not organized into a coherent group like the schismatic churches have been.
Perhaps we have milked this comparison enough - it's not really good
c.l.c. topicality. ...
On 9/18/24 04:05, David Brown wrote:
...
Perhaps we have milked this comparison enough - it's not really good
c.l.c. topicality. ...
My purpose in starting this sub-thread was to deny the validity of the comparison of this newsgroup to a Bible study group. Topicality was out
the window from that point onward.
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 17.09.2024 15:57, Tim Rentsch wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 01.09.2024 22:07, Tim Rentsch wrote:
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Is that part of a preamble or rationale given in the C standard?
That target audience would surely surprise me. Myself I've
programmed in quite some programming languages and never read a
standard document of the respective language, nor did I yet met
any programmer who have done so. All programmer folks I know used
text books to learn and look up things and specific documentation
that comes with the compiler or interpreter products. (This is of
course just a personal experience.)
I've also worked a lot with standards documents in various areas
(mainly ISO and ITU-T standards but also some others). [..]
My comment is only about the C standard, not any other standards
documents.
Yes, that was obvious.
Are trying to say that the "C standard" is substantially different
with respect to "readability" to other standards?
To other language reference documents - yes.
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 17.09.2024 15:57, Tim Rentsch wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 01.09.2024 22:07, Tim Rentsch wrote:
[...] The most important purpose of
the ISO C standard is to be read and understood by ordinary C
developers, not just compiler writers. [...]
Is that part of a preamble or rationale given in the C standard?
That target audience would surely surprise me. Myself I've
programmed in quite some programming languages and never read a
standard document of the respective language, nor did I yet met
any programmer who have done so. All programmer folks I know used
text books to learn and look up things and specific documentation
that comes with the compiler or interpreter products. (This is of
course just a personal experience.)
I've also worked a lot with standards documents in various areas
(mainly ISO and ITU-T standards but also some others). [..]
My comment is only about the C standard, not any other standards
documents.
Yes, that was obvious.
Are trying to say that the "C standard" is substantially different
with respect to "readability" to other standards?
To other language reference documents - yes.
Compared to ISO 10206 (Extended Pascal) I find C standard much
less readible. So there is difference, but in opposite direction
than you suggest. Main thing is that C standard is written in
"lawyerish" style, which is confusing to programmers. OTOH
ISO 10206 is written in precise techincal style, which is
easier. Of course, neither is a light reading.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 24:17:23 |
Calls: | 10,390 |
Calls today: | 1 |
Files: | 14,064 |
Messages: | 6,417,012 |