C is, and has always been, a hardware level oriented language. That's why it has always excelled in applications that need to be near the metal, like MCUs and operating systems. What would your new language bring to the table.
I agree - C is hardware-oriented already, which is why it lends itself to efficient use of CPUs.PL/M was a stripped down PL/1 Intel produced in the late 70s to program their small processors. PL/1 was a monster language, designed to do everything IBM could think of. PL/M extracted out a subset that was functionally comparable C, before C had become popular. Motorola made something similar called MPL. Other people went down a similar road. Its not entirely clear why everyone in the late 70s chose PL/1 as their starting point, but C killed that path in the early 80s.
What was PL/M like, and should it come back? I've never used it.
Arduino language is actually rather nice, I think; it's C with minor extensions. Maybe that's the direction to go in. Full C++ definitely isn't.
Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.This is plain stupid on modern MCUs and if you think that NOPs are a valid timing technique, then I'm not sure I want to use that language.
Support for an async/await model.There is a lot of this in the recent versions of C++. I'm not sure how useful it actually is as a language feature.
Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
I agree - C is hardware-oriented already, which is why it lends itself to efficient use of CPUs.PL/M was a stripped down PL/1 Intel produced in the late 70s to program their small processors. PL/1 was a monster language, designed to do everything IBM could think of. PL/M extracted out a subset that was functionally comparable C, before C had become popular. Motorola made something similar called MPL. Other people went down a similar road. Its not entirely clear why everyone in the late 70s chose PL/1 as their starting point, but C killed that path in the early 80s.
What was PL/M like, and should it come back? I've never used it.
Arduino language is actually rather nice, I think; it's C with minor extensions. Maybe that's the direction to go in. Full C++ definitely isn't.
Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
I can only write Microchip Assembly and thought that was pretty hardware oriented.
In accord with ataradov's comment, using NOP's to pad a routine to an exact time may be OK, so long as only a few are used. For longer delays there are other ways, not the least of which is to use a timer, e.g., TMR2, to set a beat. With such a beat, one can do other things during any appreciable delay, yet keep that time-critical routine exact -- or almost so.
PL/I was also one of the first languages used to write an operating system, some years before C arose. Contrary to popular belief too, C was created primarily for ease of implementing a compiler, programming hardware was never a goal of the designers any more than several other languages at the time.PL/1 was quite late to the "writing operating systems in a high level language" game. Which is why so few were written in it. Several languages, often described as real time oriented rather than operating system oriented, were developed for building operating systems before PL/1 existed.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C.You are very wrong here. And I think this alone is enough to stop discussion of the language design.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C.You are very wrong here. And I think this alone is enough to stop discussion of the language design.
You understand that even on simplest MCUs NOP timing is affected by flash wait states? This is not taking into account micro architectural optimizations, out of order execution, multiple issue pipelines, etc
Here is my standard way of generating small blocking delays:Code: [Select]__attribute__((noinline, section(".ramfunc")))
void delay_cycles(uint32_t cycles)
{
cycles /= 4;
asm volatile (
"1: sub %[cycles], %[cycles], #1 \n"
" nop \n"
" bne 1b \n"
: [cycles] "+l"(cycles)
);
}
Code is located in SRAM so that execution time is not subject to flash wait states.
This is better than a plain C loop, since it does not depend on the compiler and optimization settings.
It is very much true. Something like a PIC or an 8051 can run cycle exact code, and developers have gotten very used to that. Look at more modern small machines and you'll see them gradually drifting away from cycle exact operation. Even chips without an explicit cache to play with the timing often now have longish memory lines, which are demuxed in a non-deterministic way, to mitigate the slow operation of flash. Often people used to cycle exact operation struggle to adapt to this, and produce some very quirky results.Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.
These are legitimate needs arising from their problem domain, enabling these to be met by supporting it in a language is surely fully appropriate for a language designed for hardware programming.
Yes, and I had to account for the flash execution by placing the code in SRAM. And I knew exactly the platform this code would be running on, so I knew what optimizations would be performed and I knew that they would be predictable.Code is located in SRAM so that execution time is not subject to flash wait states.
Yes, and I had to account for the flash execution by placing the code in SRAM. And I knew exactly the platform this code would be running on, so I knew what optimizations would be performed and I knew that they would be predictable.Code is located in SRAM so that execution time is not subject to flash wait states.
This code would not work at all on Cortex-A5 core, for example.
It is very much true. Something like a PIC or an 8051 can run cycle exact code, and developers have gotten very used to that. Look at more modern small machines and you'll see them gradually drifting away from cycle exact operation. Even chips without an explicit cache to play with the timing often now have longish memory lines, which are demuxed in a non-deterministic way, to mitigate the slow operation of flash. Often people used to cycle exact operation struggle to adapt to this, and produce some very quirky results.Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.
These are legitimate needs arising from their problem domain, enabling these to be met by supporting it in a language is surely fully appropriate for a language designed for hardware programming.
One area where there is some merit in cycle exact code is security. If you implement something like an encryption algorithm, its much less easy to attack it if all the paths through it are of equal length. You can't make them run in equal time every time, but if you can make their averages comparable you have made the code a lot more robust against attacks.
if (A | ^B & C | ^D)
{
// do stuff
}
else
{
// do other stuff
}
exact void some_function()
{
if (A | ^B & C | ^D)
{
// do stuff
}
else
{
// do other stuff
}
}
What is the point of the language feature that won't be even theoretically possible to implement on a lot of modern hardware?
You don't need to embed the assembler, it is just a quick hack. The correct way of making delays is to use the timer. Again, not a compiler feature.
Also, have a look at existing languages before designing your own. Literally hundreds out there already. You're not the only one having tried.
For something close to C, you could have a look at Zig. See what it brings.
Before writing a compiler, try writing "real code" in your new language, just to see if it makes sense. Not just trivial code chunks.
Also, if you really want to try, I'd suggest "transpiling" to C first. Don't bother with the low-level code generation. At least not until it makes sense to do so. Additional benefit, apart from the saved time and effort, is that it will make interfacing with actual C code trivial.
PL/I was also one of the first languages used to write an operating system, some years before C arose. Contrary to popular belief too, C was created primarily for ease of implementing a compiler, programming hardware was never a goal of the designers any more than several other languages at the time.PL/1 was quite late to the "writing operating systems in a high level language" game. Which is why so few were written in it. Several languages, often described as real time oriented rather than operating system oriented, were developed for building operating systems before PL/1 existed.
C was intended to be close to the metal. That's really all a systems oriented language is.
Unlike other contemporary languages such as Pascal, C or BASIC, PL/M had no standard input or output routines. It included features targeted at the low-level hardware specific to the target microprocessors, and as such, it could support direct access to any location in memory, I/O ports and the processor interrupt flags in a very efficient manner. PL/M was the first higher level programming language for microprocessor-based computers and was the original implementation language for those parts of the CP/M operating system which were not written in assembler. Many Intel and Zilog Z80-based embedded systems were programmed in PL/M during the 1970s and 1980s. For instance, the firmware of the Service Processor component of CISC IBM AS/400 was written in PL/M.
The C language became popular not because of any inherent language prowess, (...)
The C language became popular not because of any inherent language prowess, not because of some special affinity for "hardware" level coding, but because there was no commercially available PL/I implementation available for early PCs and Microsoft had never created a compiler for it (though the did sell COBOL and FORTRAN compilers back then). When MS DOS started to dominate rather than CP/M there was no huge motive to invest in PL/I and so C started to grow because (and this is the chief reason) it is very easy to write a compiler for C. So it is an accident of history and market forces that elevated C to prominence and then of course Borland competed with Microsoft with C and then C++ emerged, leading to more attention for C.PL/M was released really early in the life of microprocessors - around 1974. C is listed as being earlier than that, but that was just internal to Bell Labs. It didn't get a real public viewing for a while. When I learned C at the end of the 70s it was still niche, while huge amounts of code for microprocessors existed in PL/M. This included CP/M itself and a lot of early CP/M applications. I was using a mature implementation of PL/M86 in 1979, when the first 8086s were available, and before MSDOS was. It had been developed and tuned with an 8086 simulator, and we did some of our development using that simulator, because the 8086 chips were not yet widely available. For quirky historical reasons the developer of MSDOS, before MS bought it, developed it in assembly language. Since PL/M86 was already available, and MSDOS was developed roughly as a CP/M clone, a lot of existing code could easily have been ported to MSDOS, but I'm not clear how much was. Microsoft misjudged the market, and put effort into Fortran and Cobol. They had to buy in a decent C compiler from Lattice when C suddenly took off. I don't think its clear why PL/M suddenly died, but it could have been Intel's licencing fees. The PC suddenly made cheap king in the software market. For people like me, who had used Algol, Coral, PL/1, PL/M and other languages for several years, C was just what we were looking for.
Too few developers stop to learn the history of such things, PL/I was not the perfect language, none are, but it did bring together some (at the time) revolutionary ideas and some of those are still unknown in modern languages, technology is as influenced by fashion as many other things in life!
The C language became popular not because of any inherent language prowess, not because of some special affinity for "hardware" level coding, but because there was no commercially available PL/I implementation available for early PCs and Microsoft had never created a compiler for it (though the did sell COBOL and FORTRAN compilers back then). When MS DOS started to dominate rather than CP/M there was no huge motive to invest in PL/I and so C started to grow because (and this is the chief reason) it is very easy to write a compiler for C. So it is an accident of history and market forces that elevated C to prominence and then of course Borland competed with Microsoft with C and then C++ emerged, leading to more attention for C.PL/M was released really early in the life of microprocessors - around 1974. C is listed as being earlier than that, but that was just internal to Bell Labs. It didn't get a real public viewing for a while. When I learned C at the end of the 70s it was still niche, while huge amounts of code for microprocessors existed in PL/M. This included CP/M itself and a lot of early CP/M applications. I was using a mature implementation of PL/M86 in 1979, when the first 8086s were available, and before MSDOS was. It had been developed and tuned with an 8086 simulator, and we did some of our development using that simulator, because the 8086 chips were not yet widely available. For quirky historical reasons the developer of MSDOS, before MS bought it, developed it in assembly language. Since PL/M86 was already available, and MSDOS was developed roughly as a CP/M clone, a lot of existing code could easily have been ported to MSDOS, but I'm not clear how much was. Microsoft misjudged the market, and put effort into Fortran and Cobol. They had to buy in a decent C compiler from Lattice when C suddenly took off. I don't think its clear why PL/M suddenly died, but it could have been Intel's licencing fees. The PC suddenly made cheap king in the software market. For people like me, who had used Algol, Coral, PL/1, PL/M and other languages for several years, C was just what we were looking for.
Too few developers stop to learn the history of such things, PL/I was not the perfect language, none are, but it did bring together some (at the time) revolutionary ideas and some of those are still unknown in modern languages, technology is as influenced by fashion as many other things in life!
Fortran on microprocessors wasn't that commercially valuable in the early days of MSDOS. Cobol was, but MS-COBOL was a dog. Things like MicroFocus's MicroCOBOL dominated the "lets move this app to a small machine" market.
You didn't mention the biggest influence on C, and the language from which its name comes - BCPL. BCPL and C are about as unlike PL/1 in their thinking as could be.
posts like this are one of the reasons why i don't want to publish or release my-c.Yet you can't stop mentioning it in every topic.
There are a handful of things I noticed when I first began to learn C back in 1990
C is, and has always been, a hardware level oriented language. That's why it has always excelled in applications that need to be near the metal, like MCUs and operating systems. What would your new language bring to the table.
There are several things that a new language would bring, here's a summary of the more salient:
- No reserved words, thus enabling new keywords to be added over time.
- Support 'bit' as a native data type.
- Support 'strings' as a native type, BCD/decimal as well.
- Support for namespaces.
- Computed gotos.
- Flexible alignment, packing and padding directives.
- Nested functions.
- Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.
- Support for an async/await model.
I never used PL/M but remember PL/1 very fondly, having used it a lot in the mid to late 1970s. It was the first programming language we encountered that did not actively try to restrict what we could do, and it suited IBM mainframes very well (that's what a lot of us were using). Most of us didn't see C until the 1980s.
A slightly cleaned up version of C would be nice, but language inventors never stop at that because it is "boring".
...
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.
There are several things that a new language would bring, here's a summary of the more salient:
- No reserved words, thus enabling new keywords to be added over time.
- Support 'bit' as a native data type.
- Support 'strings' as a native type, BCD/decimal as well.
- Support for namespaces.
- Computed gotos.
- Flexible alignment, packing and padding directives.
- Nested functions.
- Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.
- Support for an async/await model.
These are the kinds of things that I've seen other people raise or complain about sometimes, things that a freshly designed language could readily accomodate.
In my opinion, the greatest revolution in MCU programming was due to Arduino and the extremely easy and well documented SDK. This opened doors to many hobby applications, otherwise inacessible to a wide percentage of hobbyists.
Also, another suggestion: current MCU SDK's are bloated in size! If you want to program PIC, AVR and something else, you easily fill your harddisk with three huge installations.
Cache coherency, between different cores and with interrupts. That implies a memory model, as C has belatedly realised.
For many people and many purposes, it is completely appropriate to use a more powerful MCU than you strictly need, and use some of that extra speed and memory capacity to run easier to use programming languages and libraries.
And of course, arbitrary width types. Maybe I want uint5_t and int129_t. As you are building a new language, they should not be defined type names in some header file like in C, but a type system, syntax could look like uint(8 ) or something.Your wish has been graciously granted by the standard fairy, including similar syntax. (https://open-std.org/JTC1/SC22/WG14/www/docs/n3054.pdf#subsection.6.2.5)
I'm against thinking you can or should try to mix that with using NOPs for timing, or trying to force branches of a switch to have equal execution times. JUST NO. Use a faster CPU and proper event-driven programming.
Cache coherency, between different cores and with interrupts. That implies a memory model, as C has belatedly realised.
Basically as soon as dual-core PCs started to be common.
...
wouldn't have said it better.
Mentioning PICs, some of the proposed improvements are already part of the XC compilers, others are requests that clearly come from assembly programmers who can't stop thinking in assembly and ostentably write in what i call "Clever C" (forgetting that you CAN, and some time must, use separate assembly modules. Just conform to the ABI and call them as C functions so the compiler is happy, other programmers will be as well)
Dedicated strings requrie heap and personally speaking in a C project i prefer to avoid heap. In languages in which is a given i simply apply another mindset (using it responsibly of course)
A slightly cleaned up version of C would be nice, but language inventors never stop at that because it is "boring".
+1 for this, best chance for success is to take C and make small improvements to it, making it truly hardware-oriented. For example, you could add keywords like big_endian or little_endian so that compiler would handle the conversions and interfaces would look clean with no manual shifting, make bitfield ordering well defined, maybe add ranges to variables ADA/VHDL-style which would enable the compiler to do compile-time checking and optimize better.
And of course, arbitrary width types. Maybe I want uint5_t and int129_t. As you are building a new language, they should not be defined type names in some header file like in C, but a type system, syntax could look like uint(8) or something.
<typename> <identifier> ;
I've only mentioned the kinds of things raised by very experienced hardware/software engineers that rely heavily on PIC, their needs, concerns and frustrations are real, not imagined, I am simply collating broad issues and concerns raised by people who find C restrictive or inflexible.
The idea of the compiler calculating NOP's to yield equal execution time through multiple code paths sounds great until you remember interrupts, which are almost always a part of embedded systems.
What are you going to do, disable interrupts for large portions of code to prevent asynchronicity? That defeats the very purpose of hardware interrupts and essentially forces a polling based architecture that won't be compatible with a huge number of embedded environments.
I suppose you could commandeer some of the target's hardware timer resources and set up a sort of timekeeping scheme, but that smacks of certain dynamic "features" which are the worst parts of C++ and many other OO-like languages.
I'm not discouraging you from the exercise, but have realistic expectations. The chance for something like this to go mainstream is very close to zero.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.
Simple enough microcontrollers that can produce cycle-accurate predictable timing by simple instruction counting are becoming exceedingly rare. These 8-bitters still exist, but such cycle-accuracy combined to generally very crappy performance is something people do not actually want. Instead, people buy modern high-performance microcontrollers which can still produce very accurate timing by just scaling the absolute time used per cycle down, by utilizing higher clock speed. For example, a 12-cycle ISR latency on a 400MHz MCU looks like HALF a cycle on a 16MHz AVR/PIC; at that point you simply do no care if it sometimes takes 14 cycles due to some pipeline or branch mispredict or something. I have done pretty timing-sensitive things simply on interrupt handlers, and the advantage is ease of writing, reading, and maintaining that code. Manual cycle counting, or automated version thereof, is simply not needed anymore, except in very rare cases, which require careful understanding anyway.
The problem with this "predictive timing language" is, it becomes tied to certain type of hardware, and then you have replicated what the XCORE folks have done (apparently pretty well).
I am not an MCU programmer.
Why not? And why am I posting here?
Well, I am not an MCU programmer, because for my needs I don't need it and for hobby purposes it is simply said too complex for the occasional project.
I am posting here, to add a different perspective:
In my opinion, the greatest revolution in MCU programming was due to Arduino and the extremely easy and well documented SDK. This opened doors to many hobby applications, otherwise inacessible to a wide percentage of hobbyists.
So I wonder: why try to program "Yet Another C Compiler" for MCU, if the existing programmers have no problem with the existing SDK's (as the discussion so far indicates)? Why not, instead, develop an SDK/language for the masses that allow to use more generic (cheaper) MCU and program them as easily (or even more easily) as what Arduino offers?
Also, another suggestion: current MCU SDK's are bloated in size! If you want to program PIC, AVR and something else, you easily fill your harddisk with three huge installations. If you want to do something nice, make it small! Preferrably something that just deflates into a single folder.
Sorry if this is beyond your purpose or if my line of thought doesn't make sense (it does to me, though).
Regards,
Vitor
exact void some_function()
{
if (A | ^B & C | ^D)
{
// do stuff
}
else
{
// do other stuff
}
}
I've only mentioned the kinds of things raised by very experienced hardware/software engineers that rely heavily on PIC, their needs, concerns and frustrations are real, not imagined, I am simply collating broad issues and concerns raised by people who find C restrictive or inflexible.
To me, you just sound like a case of Dunning-Kruger. Those who "rely heavily on PIC" probably are "very experienced engineers" only by very flexible definition of "very experienced". Your posts have already demonstrated you having quite serious lack of basic knowledge*, probably by your learning getting hindered by excessive self-esteem. I would say: lurk moar. This forum is a great resource, read and participate in discussions on the MCU & programming subforums for 5 more years. There is a lot to learn, and by getting to read comments from some people MUCH MUCH more experienced than your PIC friends, your priority list of things you would like to see in a "C replacement" would significantly change.
*) for example, mixing totally up what "native" types mean, which is pretty important in context of hardware-oriented language!
But sure, there is a niche of cases where writing cycle accurate things on a 8-bit PIC is still relevant, and where a C replacement which can help do that with less work, is also relevant. But the result would not be "a new, hardware oriented programming language", but "a PIC-specific niche tool".
And sorry in advance for being blunt, don't take it too seriously, I just struggle to write in any other way, take it with a grain of salt.
I'm not sure why are you trying to convince us. if you think it is a good idea - do it. Get back to us when you have it working and we'll have a look at it.
This is not the first dreamy thread about ideal languages. There is no point in dreaming up more potential ideas until there is a base compiler that at least does something.
Ok, but what do you want from us then?
The only question in the OP is what we think about the existing languages. And majority of people that participated in the thread seems to agree that existing languages are sufficient to the point where there is no emergency. If something better comes up, we'll have a look at it.
Nobody wants to think of cool features you can implement in some imagined language because this is not the first and not the last thread. Nothing ever comes out of them.
...
wouldn't have said it better.
Mentioning PICs, some of the proposed improvements are already part of the XC compilers, others are requests that clearly come from assembly programmers who can't stop thinking in assembly and ostentably write in what i call "Clever C" (forgetting that you CAN, and some time must, use separate assembly modules. Just conform to the ABI and call them as C functions so the compiler is happy, other programmers will be as well)
Dedicated strings requrie heap and personally speaking in a C project i prefer to avoid heap. In languages in which is a given i simply apply another mindset (using it responsibly of course)
I've only mentioned the kinds of things raised by very experienced hardware/software engineers that rely heavily on PIC, their needs, concerns and frustrations are real, not imagined, I am simply collating broad issues and concerns raised by people who find C restrictive or inflexible.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.Code bloat is pretty much inevitable when using a high level language for a PIC or an 8051. There was one very focused C compiler for tiny cores. MicroChip bought it, and shut down things like the 8051 version, which had been quite popular for putting fairly complex apps on 8051 cores. That left the market with no dense code compilers for the 8051, and a reasonably dense compiler for the PIC. If you think code bloat is bad with the PIC compiler, you should try looking at some 8051 apps.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems.
C is not and never was designed for MCU programmingTrue, and yet it's just about the perfect "universal language" for embedded applications. It's close to the hardware without (necessarily) requiring the user to learn the specifics of the chip. Bloat is often related to a language's abstraction level, after all. Just as bloat is often related to the programmer's inexperience with the underlying hardware.
...
wouldn't have said it better.
Mentioning PICs, some of the proposed improvements are already part of the XC compilers, others are requests that clearly come from assembly programmers who can't stop thinking in assembly and ostentably write in what i call "Clever C" (forgetting that you CAN, and some time must, use separate assembly modules. Just conform to the ABI and call them as C functions so the compiler is happy, other programmers will be as well)
Dedicated strings requrie heap and personally speaking in a C project i prefer to avoid heap. In languages in which is a given i simply apply another mindset (using it responsibly of course)
I've only mentioned the kinds of things raised by very experienced hardware/software engineers that rely heavily on PIC, their needs, concerns and frustrations are real, not imagined, I am simply collating broad issues and concerns raised by people who find C restrictive or inflexible.
I do hope you aren't planning a language that is specific to current PIC processors and current PIC peripherals.
If that isn't the case, what will your strategy for inclusion/exclusion of a language feature? Either a small language that is all useful with all processors/peripherals. Or a large language that contains all possibilities, only some of which are exploitable with given processor/peripherals.
Without being able to articulate your strategy (to an audience including yourself), you will flail around in different directions.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.Code bloat is pretty much inevitable when using a high level language for a PIC or an 8051. There was one very focused C compiler for tiny cores. MicroChip bought it, and shut down things like the 8051 version, which had been quite popular for putting fairly complex apps on 8051 cores. That left the market with no dense code compilers for the 8051, and a reasonably dense compiler for the PIC. If you think code bloat is bad with the PIC compiler, you should try looking at some 8051 apps.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems.
(emphasis mine.) Again being rude (to other people, too:) sounds like peter-h is one of your very experienced engineers.
I can relate, I have written cycle-accurate code on 8-bitters and so on, but got into more challenging projects more than 15 years ago. I still believe what you propose is more like a niche tool than actually useful as a generic paradigm, or a necessity.
Old projects, on old HW, written using old-time methods - let them be, or if you absolutely must change them, find people who can work like "back then". If large changes are needed, redesign using today's HW and design principles.
I'm suspicious about the need of doing large, fundamental rewrites to 8-bit projects without hardware changes, and I'm also suspicious about the supposedly large size of these 8-bit projects. They are usually quite small, given the limitations in memory.
Squeezing out the very last drops of memory or CPU performance (micro-optimization) is specialized work anyway. If you are going to sell 10000 units you just use large enough MCU and pay some dozen cents more for it and don't need to optimize until cows come home. If you are going to sell 10000000 units, then it makes financial sense to squeeze hell out of it to save $0.10 * 10000000 = $1000000, but then you can afford a real professional who does the job in a few days and does not complain about needing a new language. Been there, done that, too.
C is not and never was designed for MCU programmingTrue, and yet it's just about the perfect "universal language" for embedded applications. It's close to the hardware without (necessarily) requiring the user to learn the specifics of the chip. Bloat is often related to a language's abstraction level, after all. Just as bloat is often related to the programmer's inexperience with the underlying hardware.
As for a "language that is [processor] specific", that's what libraries are for. And that's not limited to C, either. All the common, basic constructs are part of the language whether it's C, C++, Python, Basic, etc. Then, if you don't want to roll your own interface to the specific hardware, you grab a library. Sure it will be bloated since libraries generally try to be all things to all people, but that's the price you pay for the convenience of being abstracted (there's that word again!) from the hardware details.
I suspect all you're going to get with an "embedded language" is something that looks like C with a bunch of libraries - and that exists today. And it will be specific to that one family... so you'll have different "language versions" for various processors. Even the API won't be the same because the on-chip peripherals differ from one family to another, so code won't be "portable" except in the coarsest sense.
Consider the complexities of limited resources: I'm short one Timer, but I figure out that on this specific chip I can have it feed into a CCP module and then use the latter's interrupt to effectively synthesize an extra Timer. How are you going to make that portable? Today's available timers are 8 bits, tomorrow's are 16 bits, different families have different numbers of 8 and 16 bit timers, some of the (E)CCP modules have unique features (example: timestamping inbound CAN messages), etc. This is why there are different libraries for different families, and why most skilled embedded developers don't bother with libraries anyway.
Again, I'm not trying to be a downer here. I love your enthusiasm and passion. But you openly stated "I've not devoted much time at all to language semantics yet". Semantics and implementation are almost the same thing in embedded environments. When you're dealing with a full blown, virtual memory, isolated execution context operating system you have the luxury of living in an academia-like "ideal world" where everything is fully abstracted. Embedded systems and microcontrollers aren't like that. Properly done, every line of embedded code is written while considering the blended hardware+firmware environment because every line can have ill effects.
I think this thread can have merit if you focus on asking people what problems they have, and then go off to consider how you'd solve those situations. So far the proposed "features" would be hindrences to me and I would aggressively avoid using them.
handler = calculate_handler();
goto manage_event[handler];
I personally think your list is a mixed bag with a lot of too specific features (such as the computed gotos and the cycle-based timing) which seem odd at best, but that's just an opinion.
For a lot of the rest, this is actually available in... Ada. Problem sure is that Ada tends to yield large object code so not really adapted to small targets.
As to specifically namespaces, I agree, but I do think the "module" approach is much, much better than raw namespaces. Namespaces are a hack.
I've been saying that C lacks modules, for a long time. They introduced modules in C++ recently, but the definition they introduced is IMO a joke. Proper modules? Just look at Modula-2, Modula-3, Oberon, Ada packages. I particularly like Modula-3 modules that can be parameterized, Ada packages as well.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
That is because 8 bit PIC is utterly unsuited to running any kind of modern high level compiled language.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
That is because 8 bit PIC is utterly unsuited to running any kind of modern high level compiled language.
That can't be true Bruce, CP/M for example - an OS no less - was written in PL/I a high level compiled language, CP/M ran on the 8080, Z80 and 8085 all 8 bit devices and has been ported to other 8 bit chips too.
The device doesn't "run a language" either, it runs machine code, so long as that code has been produced sensibly for the target it will run no differently to any code surely?
Also C cannot be described as a "modern language" by any stretch, it must be fifty years old.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
That is because 8 bit PIC is utterly unsuited to running any kind of modern high level compiled language.
That can't be true Bruce, CP/M for example - an OS no less - was written in PL/I a high level compiled language, CP/M ran on the 8080, Z80 and 8085 all 8 bit devices and has been ported to other 8 bit chips too.
And this has what to do with PIC, exactly?
8080/z80 is about 100x more compiler-friendly than PIC. But about 10x less compiler-friendly than the also 8 bit 6809 or AVR.QuoteThe device doesn't "run a language" either, it runs machine code, so long as that code has been produced sensibly for the target it will run no differently to any code surely?
You're talking to someone who writes compilers, and also designs new CPU instructions. Thanks for the lesson.
8 bit PIC is fundamentally unsuited to at least the following features of modern programming languages:
- pointers and anything that is not located at an absolute address
- constant data structures in ROM
- recursive or reentrant functions
- deep call chains at all
- runtime code generationQuoteAlso C cannot be described as a "modern language" by any stretch, it must be fifty years old.
Ahead of its time, clearly.
Sadly, it is becoming more and more apparent that you don't know the first thing about either CPU instruction sets or programming languages, and you won't listen to those who do, so I'm out of this conversation.
So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".There is only so much you can do to improve architecture from 1976. PICs are really bad (by modern standard) as far as architecture goes. But people keep buying them and they have the highest profit margin. There is no incentive to do anything here.
It would include some kind of classification of language features I suppose. I suppose one could start listing features (whatever these get defined as) and then tabling these for their utility across a range of devices.I do hope you aren't planning a language that is specific to current PIC processors and current PIC peripherals.
If that isn't the case, what will your strategy for inclusion/exclusion of a language feature? Either a small language that is all useful with all processors/peripherals. Or a large language that contains all possibilities, only some of which are exploitable with given processor/peripherals.
Without being able to articulate your strategy (to an audience including yourself), you will flail around in different directions.
These are good points. No, there is no special focus on PIC products, I just wanted to include some of the problems experienced in that domain, into the overall picture, if there was some very obscure thing, specific to some very narrow device then there's little to be gained from considering that unless the effort is not too high.
As for inclusion/exclusion strategies I think that's a fascinating question and I've not thought about it in any real detail yet.
Anyway you do raise some important questions, clearly there is more to be done in this regard.
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
That is because 8 bit PIC is utterly unsuited to running any kind of modern high level compiled language.
That can't be true Bruce, CP/M for example - an OS no less - was written in PL/I a high level compiled language, CP/M ran on the 8080, Z80 and 8085 all 8 bit devices and has been ported to other 8 bit chips too.
And this has what to do with PIC, exactly?
8080/z80 is about 100x more compiler-friendly than PIC. But about 10x less compiler-friendly than the also 8 bit 6809 or AVR.
The days that you need exact execution times are long gone. Any language chasing features like that is outdated before it has been designed.That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
There is one application where exact loop timing is required: Directly generating graphic images for games. Typically the superloop will have several paths depending on which layer of the image it is generating. Sprites come to mind. It is critical that every pixel lay down at exactly the right time if the image is to make sense. This kind of thing was common on the Apple II and the Commodore 128.
Clearly, it is an old concept but it worked real well as long as the cycle count was accurate..
Who is generating graphics on the fly from the microcontroller anymore?
Atari 2600 chads. Gigatron.Ok, but that's not a typical use case. This is just doing random stuff for fun. Designing the language that would align stuff for you just for this is an overkill.
Pong on an 8 pin uC? Most people think 8 pins is relegated to 555 projects or op amps... Very retro.
That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.
Simple enough microcontrollers that can produce cycle-accurate predictable timing by simple instruction counting are becoming exceedingly rare. These 8-bitters still exist, but such cycle-accuracy combined to generally very crappy performance is something people do not actually want. Instead, people buy modern high-performance microcontrollers which can still produce very accurate timing by just scaling the absolute time used per cycle down, by utilizing higher clock speed. For example, a 12-cycle ISR latency on a 400MHz MCU looks like HALF a cycle on a 16MHz AVR/PIC; at that point you simply do no care if it sometimes takes 14 cycles due to some pipeline or branch mispredict or something. I have done pretty timing-sensitive things simply on interrupt handlers, and the advantage is ease of writing, reading, and maintaining that code. Manual cycle counting, or automated version thereof, is simply not needed anymore, except in very rare cases, which require careful understanding anyway.
The problem with this "predictive timing language" is, it becomes tied to certain type of hardware, and then you have replicated what the XCORE folks have done (apparently pretty well).
This is simply not true, I spoke recently with very experienced engineers who routinely use assembler for 8 bit PIC processors that are a large market. Trying to recode some parts of their existing, working designs in C is proving a huge challenge with code bloat and optimizations causing serious problems. Simple optimizations too like short-circuit evaluations.
All I'm arguing here really is for a new language to have features that help avoid these kinds of problems, where some aspects of the generated code are more controllable in a fine grained way, the NOP idea is just an example of what's required rather than a formal proposed language feature.
The days that you need exact execution times are long gone. Any language chasing features like that is outdated before it has been designed.That's simply not true, we can emit multiple platform specific NOP's today by embedding assembler in C. There are assembler MCU developers out there that struggle to use C because of this kind of thing. They have carefully crafted code where the want some execution path to take exactly the same number of clock cycles as some other, so they embed multiple NOPs sometimes, their designs require that.Precision timing features like emit multiple NOP operations or ensure identical execution time for (say) case clauses in a switch.That one is certainly not going to happen. Very few machines now permit cycle exact execution of anything. Even really small devices have at least some elementary kind of cache, that messes with the instruction timing.
The future is with languages that are better at dealing with complex firmware and take away pitfalls like pointers and buffer overruns from programmers so that effort can be directed at adding functionality.
Languages like Rust, Lua and Python make much more sense on a microcontroller nowadays.
Even if you are, *with proper peripherals* you absolutely do not need to have code execute with exact timings - you only need it to execute within some max time.
With modern MCUs becoming ever more powerful (good), complex (bad), and complicated (bad bad), knowing the worst case timing is becoming more difficult. In a hard realtime environment, the mean/typical timing is irrelevant.I believe, that –– perhaps surprisingly! –– distributed processing is a valid solution to this.
With modern MCUs becoming ever more powerful (good), complex (bad), and complicated (bad bad), knowing the worst case timing is becoming more difficult. In a hard realtime environment, the mean/typical timing is irrelevant.I believe, that –– perhaps surprisingly! –– distributed processing is a valid solution to this.
In a very real sense, it reduces complications, because tasks are done in separate, isolated units, that simply communicate with each other.
Message passing interfaces (with input buffers, mailboxes, and/or DMA and completion signals/flags) lets computation proceed simultaneously with communication, but it is much easier to provide/prove worst case timings for any computation.
For example, instead of integrating quadrature encoders in an MCU, I'd prefer to stick a few-cent dedicated chip on each encoder, with a shared bus to the MCU, with the MCU just accumulating the encoder state changes in a dedicated "register" per encoder. The MCU hardware peripheral is a bit more complex, but would need few pins to support even a large number of encoders. No software interrupts would be required for normal operation, so software timing would not be affected. (I'd use two addresses for each "register", though: one for normal reads, and the other for reads that clear the "register" atomically when read, for easy delta tracking without having to worry about atomicity.)
Then again, I might just be crazy. :-//
"The Zig project for me," Andrew tells us, "was peeling off these abstraction layers [of C] that we've all taken for granted and examining their foundational premises and saying, 'Is that really how it has to work? Let's question some of this stuff. Maybe it can be better'."
While investigating why features in C operate the way they do, Andrew came to realize that the reasoning behind many of them are not well justified.
Stay relevant tomorrow and learn it today :) 8)You know what?
dcl measurement_details SomeType controlled;
allocate measurement_details;
// do stuff with the variable measurement_details.
...
// allocate another.
allocate measurement_details;
free measurement_details;
// adjust the initially allocated measurement_details
...
measurement_details.totals = 10;
The best programming language for embedded already exists!
Rust is amazing an a very good fit.
The notion that C or C++ is better is simply wrong.
It's better both in term of what people prefer (stack overflow) and by the number of bugs that pass by the compiler.
Stay relevant tomorrow and learn it today :) 8)
The best programming language for embedded already exists!
Rust is amazing an a very good fit.
The notion that C or C++ is better is simply wrong.
It's better both in term of what people prefer (stack overflow) and by the number of bugs that pass by the compiler.
Stay relevant tomorrow and learn it today :) 8)
Rust is indeed popular but I learned recently of Zig and it seems it has much to offer the MCU developer.
The best programming language for embedded already exists!
Rust is amazing an a very good fit.
The notion that C or C++ is better is simply wrong.
It's better both in term of what people prefer (stack overflow) and by the number of bugs that pass by the compiler.
Stay relevant tomorrow and learn it today :) 8)
Rust is indeed popular but I learned recently of Zig and it seems it has much to offer the MCU developer.
I had a quick scan of the Zig blog you referred to, and I wasn't impressed. To over-simplify, it seemed to be too hung up on a few bits of syntax, and didn't address the semantics of the languages or the appropriate use-cases for languages.
Fundamentally syntax isn't very interesting: I can learn to type different things to represent the same concepts - but I will only do so if the language semantics give me some real advantages over existing languages. Hence I avoided learning Delphi because it didn't offer much over C. I ignored C# since it offers nothing over Java (and introduces significant problems too). Etc etc.
To over-simplify again, semantics is about "what don't I have to worry about because I know the language will correctly deal with it on my behalf as my programs get bigger and more complicated". One such example is memory management. Another is parallel processing, especially with many cores/processors. Another is "will my code meet the hard realtime deadline". And, of course, the interactions between all those.
The prevalence of buffer overflow/underflow/overrun bugs in C is at least partly explained in that the fundamental unit of reference in the libraries is an address instead of an address range.
If we look at languages that natively support slicing, they conceptually use tuples (origin, step, length) or (origin, step, min-index, max-index) to describe array slices.
While current C compilers can check for array boundaries at compile time (with or without generating run-time code that checks the array boundaries at run time), the library interfaces are such that such compile time checks do not apply across API boundaries.
While interfaces like returntype functionname(size_t len, datatype data[len]) are supported since C99 –– data then has a variably modified type –– and compilers can detect simple overrun cases at compile time (and generate runtime bounds checking code) both for callers and callees using this kind of interface, they are extremely rare in practice: instead, returntype functionname(datatype *data, size_t len) is used, and this defeats the compile-time bounds checking, since the compiler cannot know that len is intended to be the length of the data array. For the array slicing approach, only the latter (non-bounds-checkable) type of interface is possible.
One might argue that a language with C semantics, except replacing pointers with arrays so that pointers decay to arrays of size 1 with step zero, instead of arrays decaying to pointers to their first element, and with an optional standard library that uses the array slice approach, could be an obvious but relatively straightforward solution to the entire memory access and buffer "problem".
As to the actual generated machine code, one would still need a way to provide optimized implementations for specific step sizes for the same function, so some kind of conditional function overloading (which is not supported in C) would help. Also, moving type, variable, and function (or scope) attributes into the syntax itself (perhaps similar to C++ [[attribute]] syntax) would be nice.
So, I don't really see that much wrong in C per se; it's mostly the C standard library that bugs me.
I don't really get the point of this keyword thing. Of course any language will limit the number of predefined keywords to avoid making it increasingly hard not to clash with users' identifiers.
OTOH, if you define very little to (at the extreme) no keyword in the language, you'll have to resort to cryptic base constructs using 'symbols' for your language syntax. This has been done before. This usually isn't pretty, but hey, beauty is in the eye of the beholder.
Not sure what you mean by "extensible". There have been hundreds of different ways of making a language "extensible", with various levels of success. Could you give examples of "extending", for instance, C, that would be actually useful? Extending the language on the syntax level is a slippery slope. It means basically that your language would be a constant moving target. Rarely a good thing. Now if by that you rather mean some macro system that is more elaborate than just text substitution, then sure. Why not. You'll find that in other languages already. Certainly the C preprocessor is very crude. But it's very flexible at the same time. So beware of trying to design a better macro system that would replace it: to get half of its flexibility, you're very likely to end up with an extremely complex system.
Regarding pointers, that's one of the key flexibility of C, but of course with power comes responsibility, as they say. Nobody forces you to use pointers in weird ways. You can even decide not to do any pointer arithmetic at all if you feel uncomfortable with that, and still write useful C.
Reading this thread, it all looks like you (and others) are pointing out roughly the same things that have been over and over again. I don't want to get you discouraged, but I'm failing to see what could be possibly new here.
While creating a new language is something that piques the interest of many if not most people writing software at some point in their life, most end up giving up and getting back to writing software. Getting around/doing with C quirks usually end up being more pragmatic and more productive than trying to create your own language, which in the end may not prove any better.
Or, you do as DiTBho describes. You write your own tools to fit your own needs, in which case you don't ask for others' opinions because they will not get you anywhere anyway - that would be like asking people what their favorite color is in order for you to pick the color of your next car. Just a thought.
I did create a "design a better C" thread quite a while ago as an experiment, and while there were a few interesting points, it went nowhere as expected. People more or less all have gripes about C, but when it comes to defining what exactly would make it better, it's suddenly a lot of silence, with the expected occasional "use Rust instead". ::)
goto target[index];
target(1):
// code
target(2):
// code
iterator int get_randoms()
{
for (int I=0; I < 100; I++)
{
yield random(I);
}
}
main()
{
foreach (R in get_random())
{
// do stuff
}
}
AdaCore is partnering with NVIDIA™ to implement Ada and SPARK programming languages for select safety-critical firmware used in their next-generation embedded systems platforms. Customer using products such as NVIDIA® Jetson™ for embedded artificial intelligence (AI) computing, NVIDIA DRIVE™ AGX for autonomous vehicle (AV) development, and Isaac™ for Robotics are able to use the same technology at the application level. At the core of NVIDIA’s embedded systems is a system on a chip (SoC) that integrates an ARM architecture central processing unit (CPU), graphics processing unit (GPU), memory controller and IO paths onto one package. AdaCore’s GNAT Pro Ada and SPARK compilers can compile code targeting the ARM CPU embedded on NVIDIA SoCs, and increase verification efficiencies to achieve compliance with the functional safety standard ISO-26262. Supported compilation modes include bare metal as well as Linux or QNX OSes.
but in C its impossible to add these without being unable to compile earlier code.This is not true and C is expanded all the time. C specification clearly defines what identifiers (stuff that starts with underscore and a capital letter, and a number of other cases) are reserved for future use and those are used to expand the language.
Well that's fine, at least you've looked at it, its a rich subject and a huge source of healthy disagreement!
Speaking of disagreement, you say "I ignored C# since it offers nothing over Java (and introduces significant problems too)".
I'd be interested in hearing more on the problems you found.
I'm not aware any linguistic advantage Java has over C#, Java is not standardized either whereas C# is defined by an international, non proprietary standard.
Java (because it is based on the C grammar) find its very hard to add new keywords and grammar changes, for example Java does not have properties, so one has to code get_ and set_ or some other contrivance.
Java does not have generics, in C# generics are instantiated at runtime, Java has a limited compile-time form of generics (type erasure) with consequent serious limitations.
C# has partial classes greatly simplifying system that rely in custom generated source code.
C# has indexers.
C# has ref/out argument passing and supports optional arguments and positional arguments, not so Java.
C# has superb interop capabilities Java has very little to offer.
C# offers pointers for scenarios where code must interact with memory not managed by the CLR.
C# supports iterators, async iterators, async and await as language concepts.
C# has come a long way over the past twenty years, my only criticism of C# is that it might be getting a bit unwieldy and some of that is due to (IMHO) the fact that it too, presumed the C grammar rather than a fresh clean sheet of paper approach.
Also you say "syntax isn't very interesting" and I don't know what that means, do you mean you don't care at all about notational aspects of a language? We reason about code by looking at it, a good syntax improves one's ability to reason over a poor syntax.
This is why mathematics succeeds, it us superb when it comes to notation, we can look at some mathematical equation and reason about it, but express the same equation in assembler or C and we lose that ability to a great extent.
The king of languages from the standpoint of notation is APL, initially that was an experiment in a new mathematical notation for certain types of problems, later it became a programming language and remains to this day the pinnacle of expressivity.
Iverson - who invented APL - wrote a superb paper on this, it was a talk he gave after receiving the Turing Award for his contribution to computer science, syntax is everything to Iverson.
Notation as a tool of thought (https://www.jsoftware.com/papers/tot.htm).
In a similar way lambda calculus is better at expressing certain ideas than a Turing machine is.
If you are going to insult the fine Scottish people of the 1970s like that, you are clearly a troll.So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".
but in C its impossible to add these without being unable to compile earlier code.This is not true and C is expanded all the time. C specification clearly defines what identifiers (stuff that starts with underscore and a capital letter, and a number of other cases) are reserved for future use and those are used to expand the language.
return (result);
yield (item);
yield return (item);
yield (item);
The features you want (like iterators) would imply mandatory heap allocation and other behaviour that C avoids because it is not possible on all platforms (including small PICs).
It is good to want to have those high level language features in a low leve language, but it is impossible or impractical to actually implement them. So, when typing out the sample code you want to work, also type out the assembly you want that to translate into to see potential problems.
And even when things may conflict, C standard authors may still introduce new things after assessment of potential impact. For example C23 would introduce a new keyword "constexpr", which would make some old code break.
a = substring("this can't be real",4);
f = frameptr(4); // get pointer to the 4th window frame.
dcl frameptr builtin; // dcl the function to walk up the stack frames.
// other code...
dcl cos builtin;
dcl sin builtin;
dcl substring builtin;
dcl max builtin;
dcl sqrt builtin;
OK what about label variables an aspect of computed gotoAlready supported in C by GCC, Clang, and Intel CC at least, via labels as values (https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html#Labels-as-Values). For example,
int is_even(int x)
{
static const void *const target[2] = { &&even, &&odd };
goto *target[x & 1];
odd:
return 0;
even:
return 1;
}
Or iterators like we see in C#Those can be implemented via C coroutine (https://en.wikipedia.org/wiki/Coroutine#C) support. Granted, you need either POSIX or SuS support, or an assembly (machine code) implementation, to be able to obtain and swap stack and register file context, but many library implementations for C do exist.
OK what about label variables an aspect of computed gotoAlready supported in C by GCC, Clang, and Intel CC at least, via labels as values (https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html#Labels-as-Values). For example,Code: [Select]int is_even(int x)
{
static const void *const target[2] = { &&even, &&odd };
goto *target[x & 1];
odd:
return 0;
even:
return 1;
}Or iterators like we see in C#Those can be implemented via C coroutine (https://en.wikipedia.org/wiki/Coroutine#C) support. Granted, you need either POSIX or SuS support, or an assembly (machine code) implementation, to be able to obtain and swap stack and register file context, but they do exist. Essentially, "yield()" and "next()" form a coroutine pair, where calling one switches execution to the other.
In a low level language, the implicit context swap would be a Big Problem. It is like exceptions in C++, which tend to be too resource intensive to be worth implementing in microcontroller environments. In a microcontroller, available stack is often counted in bytes, so having two or more stacks at the same time is a no-no.
goto target(x+1);
target(0):
return 0;
target(1):
return 1;
You are focusing way too much on "nice looking" syntax. This is not the most important part. If your code is full of those computed gotos, then you are likely doing something wrong anyway.
And I'm lost again what is being discussed. You throw some random examples of code that you don't want to work.
Yes, it is possible to make a language exactly as you like, but that happens by opening a text editor and typing the code, not forum posts.
If you are going to insult the fine Scottish people of the 1970s like that, you are clearly a troll.So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".
The PIC was developed in Glenrothes. I think the Picts were farther north than that.If you are going to insult the fine Scottish people of the 1970s like that, you are clearly a troll.So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".
Some variety of small furry animal for sure.
And I think you mean "Pict".
If you are going to insult the fine Scottish people of the 1970s like that, you are clearly a troll.So before making sweeping pronouncements like "8 bit PIC is utterly unsuited to running any kind of modern high level compiled language" you need to ask yourself why Microchip are seemingly unaware of this.
I can assure you, Microchip is painfully well aware of this.
Would you say then that this could also apply to them "it is becoming more and more apparent that they don't know the first thing about either CPU instruction sets or programming languages, and they won't listen to those who do".
(instead of trolling)I'm not trolling, I've just seen this exact thread dozens of times. Nothing practical will come out of it.
Here's how it could look:Me no like. How do you do range checking on x? And the syntax looks too much like function calls to me. The colon at the end is so easy to confuse with a semicolon. Obfuscated C material, methinks...
I do not always agree with ataradov, but here, he has a particularly apt point.You are focusing way too much on "nice looking" syntax. This is not the most important part. If your code is full of those computed gotos, then you are likely doing something wrong anyway.You could be contributing to the conversation (instead of trolling) as several other people are
And I'm lost again what is being discussed. You throw some random examples of code that you don't want to work.
Yes, it is possible to make a language exactly as you like, but that happens by opening a text editor and typing the code, not forum posts.
(instead of trolling)I'm not trolling, I've just seen this exact thread dozens of times. Nothing practical will come out of it.
If the goal is to get generally educated on why certain compilers do something one way or the other, I'm perfectly fine with that. But you start your claims with stuff like "C can't do this" or "PIC authors don't know", which are all incorrect.
And that discussion of Scots is sure on-topic here.
Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?
https://www.adacore.com/nvidia (https://www.adacore.com/nvidia)
Maybe they are just trying to sidestep ageism, or create opportunities for ex-defence people, and hire some people old enough to have actually used Ada. :)Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?That seems reasonable. Much more so than switching to uh, Rust, for instance. ;D
https://www.adacore.com/nvidia (https://www.adacore.com/nvidia)
Did no one get my troll ... Pict reference? :-( :-(Have you been grooving with one?
Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?
https://www.adacore.com/nvidia (https://www.adacore.com/nvidia)
That seems reasonable. Much more so than switching to uh, Rust, for instance. ;D
You are focusing way too much on "nice looking" syntax. This is not the most important part. If your code is full of those computed gotos, then you are likely doing something wrong anyway.
And I'm lost again what is being discussed. You throw some random examples of code that you don't want to work.
Yes, it is possible to make a language exactly as you like, but that happens by opening a text editor and typing the code, not forum posts.
You could be contributing to the conversation (instead of trolling) as several other people are, I'm sure you have much to offer but it seems you simply resent me discussing the subject. Several posts have contained genuinely useful responses, things that give me pause for thought.
Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?
https://www.adacore.com/nvidia (https://www.adacore.com/nvidia)
That seems reasonable. Much more so than switching to uh, Rust, for instance. ;D
Hot off the press: "The U.S. NSA finally came out this week to strongly endorse `memory-safe' languages for most software programming, specifically mentioning C#, Go, Java, Ruby, Rust, and Swift as examples. Apparently orphaned DoD language *Ada* was conspicuously left out of NSA's list, even though versions of Ada that target JVM can utilize Java JVM's GC."
FFI see http://catless.ncl.ac.uk/Risks/33/53#subj3 (http://catless.ncl.ac.uk/Risks/33/53#subj3)
If a language doesn't reduce the chances of something appearing in that comp.risks, then I'm unlikely to want to swap to it.
Did no one get my troll ... Pict reference? :-( :-(Have you been grooving with one?
Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?
[Professionals who enable all this crazy hightech are just fine working with C, after all, and don't like managers messing up their good productivity for social scores.
Well, do178b/misra95 C code has the same "it's done" characteristic.
Removing naughty bits is far more important that fiddling with syntax. Unless, of course, it is the syntax that is the source of "inappropriate" behaviour.
Productively producing bugs that could've been avoided.
Productively producing bugs that could've been avoided.
Language purists are interesting folks, because they promise you the Moon from the sky, and completely bug-free systems. Compare that to non-programming fields! How about an aircraft engineer who claims they never make any mistakes? Anyone heard about such ridiculous claim?
For some strange reason, the track record for these "perfect" languages is near-zero. It's always "C causes bugs, <X> is perfectly bug-free, oh why are all the professionals so stupid they don't use <X> but stick to C, boo hoo hoo".
Talk is cheap. C is indeed quite crappy but most of the dangerous aspects are very well known to the professionals and avoided. They know their tools, and despite all the shortcomings of C, its strongest points are standardization, the fact it's not a moving target, being very well known, something to build expertise on.
Surgeons also use knives despite the fact you need to use them carefully. There are no wannabe-surgeons who complain all the time and have little to show, though. That's the difference when compared to computer programming.
Look at Ada, did you know that Nvidia recently decided to start sidelining C and C++ for all future firmware development in favor of Ada?
https://www.adacore.com/nvidia (https://www.adacore.com/nvidia)
That seems reasonable. Much more so than switching to uh, Rust, for instance. ;D
Hot off the press: "The U.S. NSA finally came out this week to strongly endorse `memory-safe' languages for most software programming, specifically mentioning C#, Go, Java, Ruby, Rust, and Swift as examples. Apparently orphaned DoD language *Ada* was conspicuously left out of NSA's list, even though versions of Ada that target JVM can utilize Java JVM's GC."
FFI see http://catless.ncl.ac.uk/Risks/33/53#subj3 (http://catless.ncl.ac.uk/Risks/33/53#subj3)
If a language doesn't reduce the chances of something appearing in that comp.risks, then I'm unlikely to want to swap to it.
Well, do178b/misra95 C code has the same "it's done" characteristic.
Both that and Rust are C with the naughty bits forbidden >:D Ditto SPARK and Ada :)
No, I'm not going to attempt to fully justify those statements, but there is some truth in them.
Removing naughty bits is far more important that fiddling with syntax. Unless, of course, it is the syntax that is the source of "inappropriate" behaviour.
Language purists are interesting folks, because they promise you the Moon from the sky, and completely bug-free systems. Compare that to non-programming fields! How about an aircraft engineer who claims they never make any mistakes? Anyone heard about such ridiculous claim?
and despite all the shortcomings of C, its strongest points are standardization, the fact it's not a moving target, being very well known, something to build expertise on.
Surgeons also use knives despite the fact you need to use them carefully. There are no wannabe-surgeons who complain all the time and have little to show, though. That's the difference when compared to computer programming.
You won't fix anything this way.
Necessary but not sufficient.
You won't fix anything this way.
Necessary but not sufficient.
Precisely!
It seems to be axiomatic that digital hardware engineers yearn to create their own processor, and software engineers yearn to create their own language. Without a lot of industry knowledge plus clarity and insight, both are doomed to being (at best) an interesting academic exercise.
You say "talk is cheap" yet that entire post is nothing but anecdotes, how about facts, metrics, objective data?
Isn't it obvious that all the implementation dependent behaviour isn't standardised?!
And then there's all the undefined behaviour
Surgeons have to be rigorously trained and pass exams, and are regularly monitored by formal regulatory bodies. Anybody can call themselves a programmer and practice [sic] it without being monitored.
You say "talk is cheap" yet that entire post is nothing but anecdotes, how about facts, metrics, objective data?
FWIW, to me the one single objective datapoint is I professionally develop firmwares for embedded devices in C and have absolutely no problem with it whatsoever and do not complain about it lacking anything critical, or generating critical bugs for me. I haven't stepped in any C footgun in years, just make classical bugs (wrong assumptions, logical mistakes) I would do in any language, and not too many. I also don't mind supporting my code, it's not fire-and-forget.
I know a few others in my field who also do not struggle. But I'm not going to give names, just like you are not giving the names of your "very experts" who struggle with PICs.
You say "talk is cheap" yet that entire post is nothing but anecdotes, how about facts, metrics, objective data?
FWIW, to me the one single objective datapoint is I professionally develop firmwares for embedded devices in C and have absolutely no problem with it whatsoever and do not complain about it lacking anything critical, or generating critical bugs for me. I haven't stepped in any C footgun in years, just make classical bugs (wrong assumptions, logical mistakes) I would do in any language, and not too many. I also don't mind supporting my code, it's not fire-and-forget.
I know a few others in my field who also do not struggle. But I'm not going to give names, just like you are not giving the names of your "very experts" who struggle with PICs.
Right, so no facts to backup the original anecdotal remarks and now the rather obvious claim that there are many engineers who are quite content with C. None of that has any bearing on what's being discussed, obviously it is not those who are content, that inspire or generate progress.
Isn't it obvious that all the implementation dependent behaviour isn't standardised?!
What weird mental acrobatics. It being in the standard means it is standardized! Standard basically says: we did not define the effect of this operation, so don't do that unless you really know what you are doing.
Would be very nice if implementation-defined behavior was not needed in the standard, but it's still much better to have standardized implementation-defined behavior than the standard being completely silent about some corner cases. This makes C standard good, compared to poorly standardized languages where you can't know what happens by reading the standard.QuoteAnd then there's all the undefined behaviour
Exactly the same for UB! This is the strong point of C: UB being standardized, so just read and understand the standard, don't do UB, and you are good to go.
Having such standard is not obvious at all.QuoteSurgeons have to be rigorously trained and pass exams, and are regularly monitored by formal regulatory bodies. Anybody can call themselves a programmer and practice [sic] it without being monitored.
But anyone can also buy surgical knives freely and do whatever with them. And doing something with C where human lives are at a stake, say design an autopilot for a passenger aircraft, also requires at least some form of monitoring by formal regulatory bodies. Anyone is free to use surgical knife to peel an apple, even if that poses a risk of cutting oneself. My surgeon analogue is thus valid.
You say "talk is cheap" yet that entire post is nothing but anecdotes, how about facts, metrics, objective data?
FWIW, to me the one single objective datapoint is I professionally develop firmwares for embedded devices in C and have absolutely no problem with it whatsoever and do not complain about it lacking anything critical, or generating critical bugs for me. I haven't stepped in any C footgun in years, just make classical bugs (wrong assumptions, logical mistakes) I would do in any language, and not too many. I also don't mind supporting my code, it's not fire-and-forget.
I know a few others in my field who also do not struggle. But I'm not going to give names, just like you are not giving the names of your "very experts" who struggle with PICs.
Would be very nice if implementation-defined behavior was not needed in the standard, but it's still much better to have standardized implementation-defined behavior than the standard being completely silent about some corner cases.In my opinion, it is the implementation-defined behaviour that lets C evolve users first, as opposed to standards folk dictating the evolution to users and compiler developers. It is closely intertwined with the C extensions, but I see the IB as giving leeway to change, and extensions as new growth.
Seems like the python thread all over again.
A "better" programming language will not help you become a better programmer. It is not about the language, it is the mindset that makes a good programmer. The ability to understand why something does not work as intended and then fix it, is what counts.
For me plain C without libraries and HAL work very well on bare metal embedded projects. Yes you have to do everything yourself, but over time you build up your own set of code that can be used over and over again. The benefit of this is that you know what everything does.
I have seen things go sideways with for example C#, where a lot is done for you, and it can take a lot of effort to fix it when it turns out to be a bug in the libraries that come with it. Also had to work around bugs in javascript that can take a lot of time to figure out why things go wrong. Even with C++ and then perform bound checks, I have seen memory leaks within the libraries.
But like others wrote, if you like doing something like this go ahead, but I expect that not a lot of the expert embedded developers want to become testers of your new language.
Most of the complaining here is not about specific suggestions about language improvements or changes,Once again, this is not the case. All your suggestions were addressed and discussed.
daring to rock the boatYou would rock the boat if you come up even with a prototype compiler or coherent description of the proposed language. You are not the first one to want a new better language. Some people did the work and ended up with Zig and Rust and a ton of others. If you don't like them, propose what you would change. But if all you would change is a syntax for the computed gotos, then don't blame us that we don't care.
Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Most of the complaining here is not about specific suggestions about language improvements or changes,Once again, this is not the case. All your suggestions were addressed and discussed.daring to rock the boatYou would rock the boat if you come up even with a prototype compiler or coherent description of the proposed language. You are not the first one to want a new better language. Some people did the work and ended up with Zig and Rust and a ton of others. If you don't like them, propose what you would change. But if all you would change is a syntax for the computed gotos, then don't blame us that we don't care.
Do the work, propose something concrete to discuss and we will gladly do so. Right now all we have is a set of random ideas.
Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Not quite.
It is pointing out that if someone suggests jumping out of the frying pan, they should have good reasons why they aren't jumping into the fire. Or some similar tortured analogy!
Everything you proposed here is trivial and was already known, none of this is enough to justify making a new language or even extending the exiting ones.
Most people here are engineers, and everyone participating in this thread expressed their opinion on your proposals. This is the engineering opining based on people's experience with using the exiting tools. And the consensus seems to be that none of this is really necessary. May be nice to have if it was already there, but not necessary. What else is to discuss here?
If you want more safety and better defined behaviour, use Rust. Many companies and individuals are switching to it instead of C.
this is an informal discussion about ways one might produce a programming language for MCU use.At this thread shows, no one here is interested in a language specifically for MCUs. Why would we? What if your next project needs and MPU as well (as increasingly happens)? Whys would we not what to use the same exact tool set?
this is an informal discussion about ways one might produce a programming language for MCU use.At this thread shows, no one here is interested in a language specifically for MCUs. Why would we? What if your next project needs and MPU as well (as increasingly happens)? Whys would we not what to use the same exact tool set?
"Elephant in the room" here is that we don't see a point in discussing this topic. Not because we are entrenched with C and refuse to even look at something else. Most of us looked at other stuff a lot, and in the end C ends up being the best option when you consider everything, not just a very narrow use case of programming low level PICs.
Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
There is no surprise that there are engineers quite content with C, or even committed to C.When talking about low level programming languages –– which is what I understand 'a hardware "oriented" programming language' to mean ––, C is just the one with the best proven track record decades long. It isn't that great, it's just the 'benchmark' one for others, due to its widespread use and role in systems programming and embedded development.
I get that some consider this pointlessNo, that's not it at all. Not pointless, more like bass-ackwards. We want the results too, we just have seen your approach before leading to nowhere. We're trying to steer you to not repeat that, but actually produce something interesting.
What - specifically - do you find problematic with C# ? do you have an actual, real world example you can share?
Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Bullshit.
It's frequently my job to "rock the boat" by proposing changes and improvements to processes, libraries, programming languages, and even new machine code instructions. You can find my name for example in the credits for the RISC-V base instruction set and the B and V extensions.
Your proposed changes address ONE aspect of the problem:
- does the change make a programmer's life slightly more convenient?
You totally ignore every other aspect, such as:
- can the proposed feature be efficiently implemented on the target devices?
- is it a significant improvement vs using a library (function, macro) or maybe code generator instead?
- is the product of the improvement and the frequency of use sufficient to justify the incremental cumulative increase in complexity of the language, compiler, manuals, training?
When diving into the Internet Of Things this becomes more in play, but then you start to need beefier microcontrollers with more memory to get things done. And sure for a lot of programmers it will be beneficial to have a language that does all the hard work for them, but when shit hits the fan, they will have an even harder time to solve the problem. That is why I, and probably ataradov, like C so much. If you don't want it, you can keep all the "luxury" out the door.
For instance, with the Arduino setup you can write up something quickly and have it work, but the moment you need something special it becomes harder to realize it, because of the lack of transparency. And yes I have looked under the hood of the Arduino system and it is diving into a lot of sub directories and header files to find something specific. Same with the STM32 HAL code. In the end I find it easier to just read the reference manual of a microcontroller and write code for it at the lowest level possible without resorting to assembler.
Why did SiliconWizard's Design a better "C" (https://www.eevblog.com/forum/programming/design-a-better-c/) thread 'not go anywhere'? It just sprawled around, with individual features and other languages discussed. In fact, it really showed how complicated and hard it is to do better than C from scratch; with other languages like Ada (https://en.wikipedia.org/wiki/Ada_(programming_language)) discussed but nobody knowing exactly why they never got as much traction as C. Just consider this post by brucehoult (https://www.eevblog.com/forum/programming/design-a-better-c/msg3618621/#msg3618621) about midway in the thread, about how C with its warts and all still maps to different hardware so well.
unsigned long add_carry(unsigned long x, unsigned long y, unsigned long a, unsigned long b){
return (a+b) < a ? x+y+1 : x+y;
}
mov rax, rdi
add rdx, rcx
adc rax, rsi
ret
cmn r2, r3
it cs
addcs r1, r1, #1
add r0, r0, r1
bx lr
cmn x2, x3
adc x0, x1, x0
ret
not a2, a2
sltu a2, a2, a3
add a0, a0, a1
add a0, a0, a2
ret
A need for a hw-oriented language? There is the Forth :D
Even for the people who just want Carry, I highly doubt they are prepared for the different implementations of the carry flag with respect to subtraction and compare.
And it's simply not hard to express what you ACTUALLY WANT in portable C anyway. The compiler will automatically use the flags where available.
Even for the people who just want Carry, I highly doubt they are prepared for the different implementations of the carry flag with respect to subtraction and compare.
And it's simply not hard to express what you ACTUALLY WANT in portable C anyway. The compiler will automatically use the flags where available.
Bignums aren't easy in portable C :)
Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Bullshit.
It's frequently my job to "rock the boat" by proposing changes and improvements to processes, libraries, programming languages, and even new machine code instructions. You can find my name for example in the credits for the RISC-V base instruction set and the B and V extensions.
Your proposed changes address ONE aspect of the problem:
- does the change make a programmer's life slightly more convenient?
You totally ignore every other aspect, such as:
- can the proposed feature be efficiently implemented on the target devices?
- is it a significant improvement vs using a library (function, macro) or maybe code generator instead?
- is the product of the improvement and the frequency of use sufficient to justify the incremental cumulative increase in complexity of the language, compiler, manuals, training?
I used a naked "8-pin Arduino" in my Vetinari clock (https://entertaininghacks.wordpress.com/2015/02/23/vetinari-digital-clock/), because that was the quick way to get an undemanding result.That's exactly why ATmega32u4 is one of my favourite 8-bitters. It has native full-speed USB (12 Mbit/s, can do 1 Mbyte/sec even over USB Serial), but still is completely programmable without any HAL or similar, on bare metal, using nothing but avr-gcc (and avr-libc, if you really want), in (freestanding) C or C++.
But when making something that required very low power consumption, I simply wrote C to peek/poke the atmega328 registers. That avoided me having to work out how Arduino libraries might be frustrating me.
I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.
Oh come on, now you're just sounding like a petulant child that is disappointed that someone is pointing out a flaw in their Perfect Plan yourself; one that is expecting adulation and praise. Get your ego out of the discussion, and concentrate on the subject matter instead.Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Bullshit.
It's frequently my job to "rock the boat" by proposing changes and improvements to processes, libraries, programming languages, and even new machine code instructions. You can find my name for example in the credits for the RISC-V base instruction set and the B and V extensions.
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
Oh come on, now you're just sounding like a petulant child that is disappointed that someone is pointing out a flaw in their Perfect Plan yourself; one that is expecting adulation and praise. Get your ego out of the discussion, and concentrate on the subject matter instead.Most of the complaining here is not about specific suggestions about language improvements or changes, but more about the fact that someone is actually daring to rock the boat and discuss the subject dispassionately, point out the elephant in the room!
Bullshit.
It's frequently my job to "rock the boat" by proposing changes and improvements to processes, libraries, programming languages, and even new machine code instructions. You can find my name for example in the credits for the RISC-V base instruction set and the B and V extensions.
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
Bignums aren't easy in portable C :)If your limbs are of uint32_t type, you can use uint_fast64_t for the intermediate results. On architectures without a 32×32=64-bit unsigned multiplication or 64/32=32+32 unsigned division with remainder, the machine code will be pretty inefficient, but it will work.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?I'm saying that they are not at all important to microcontroller or embedded developers at all.
There is no surprise that there are engineers quite content with C, or even committed to C.
When talking about low level programming languages –– which is what I understand 'a hardware "oriented" programming language' to mean ––, C is just the one with the best proven track record decades long. It isn't that great, it's just the 'benchmark' one for others, due to its widespread use and role in systems programming and embedded development.
For examples of nearly bug-free programs written in C in the systems programming domain, go check out D. J. Bernstein ([url]https://cr.yp.to/djb.html[/url])'s djbdns ([url]https://cr.yp.to/djbdns.html[/url]), qmail ([url]https://cr.yp.to/qmail.html[/url]), daemontools ([url]https://cr.yp.to/daemontools.html[/url]), cdb ([url]https://cr.yp.to/cdb.html[/url]). This is the guy behind Curve25519 ([url]https://en.wikipedia.org/wiki/Curve25519[/url]), having released it in 2005.
Like it, dislike it, doesn't matter, C is just a tool. But as a tool, its features and track record are significant. So are its deficiencies, and that means any real effort to do better is valuable.
In comparison, C# is a managed language. .NET Micro requires at least 256k of RAM. .NET nanoFramework requires at least 64k of RAM, and runs on Cortex-M and RISC-V (ESP32-C3) cores. So, perhaps suitable for medium to large embedded devices, but decidedly unsuitable for small ARMs and anything less than 32-bit architecures.
Ada can be used to program AVR 8-bit microcontrollers (see AVR-Ada), but it is still relatively little used. One possible reason is that while GCC GNAT is GPL3+ licensed with a runtime library exception, AdaCore sells GNAT Pro, and the FSF/GCC GNAT is seen as "inferior", with the "proper" version being the sole product of a commercial company. (Or maybe that's just me.)I get that some consider this pointless
No, that's not it at all. Not pointless, more like bass-ackwards. We want the results too, we just have seen your approach before leading to nowhere. We're trying to steer you to not repeat that, but actually produce something interesting.
If you start a language design from scratch, you must understand the amount of design choices already made for existing languages. The ones in languages that have survived use in anger, are the ones where the choices support a programming paradigm the users find intuitive and effective.
Why did DiTBHo not start from scratch, and instead pared down C to a subset with some changes and additions, to arrive at their my-C, designed for strictly controlled and enforced embedded use cases? Because they needed a tool fit for a purpose, and it was a straightforward way to achieve it. Results matter.
Why did SiliconWizard's Design a better "C" ([url]https://www.eevblog.com/forum/programming/design-a-better-c/[/url]) thread 'not go anywhere'? It just sprawled around, with individual features and other languages discussed. In fact, it really showed how complicated and hard it is to do better than C from scratch; with other languages like Ada ([url]https://en.wikipedia.org/wiki/Ada_(programming_language))[/url] discussed but nobody knowing exactly why they never got as much traction as C. Just consider this post by brucehoult ([url]https://www.eevblog.com/forum/programming/design-a-better-c/msg3618621/#msg3618621[/url]) about midway in the thread, about how C with its warts and all still maps to different hardware so well.
Me, I have worked on replacing the standard C library with something better. Because the C standard defines freestanding environment where the C standard library is not available in quite a detail –– unlike say C++, which also has the same concept, but leaves it basically completely up to implementations to define what it means ––, this is doable. I aim to fix many of the issues others have with C. With C23 around the corner, the one change I think might actually make a difference is to arrays not decay to pointers, and instead conceptually use arrays everywhere to describe memory ranges. Even just allowing type variation based on a later variable in the same argument list would make it possible to replace buffer overrun prone standard library functions with almost identical replacements, that would allow the C compiler to detect buffer under- and overruns at compile time. It would only be a small addition, perhaps a builtin, to make it possible to prove via static analysis that all memory accesses are valid.
In other words, I'm looking to change the parts of C that hinder me or others, not start from scratch.
Am I a C fanboi? No. If you look at my posting history, you'll see that I actually recommend using an interpreted language, currently Python, for user interfaces (for multiple reasons).
I currently use C for some embedded (AVRs, mainly), and a mixed C/C++ freestanding environment for embedded ARM development; I also use POSIX C for systems programming in Linux (mostly on x86-64). (I sometimes do secure programming, dealing with privileges and capabilities; got some experience as a sysadmin at a couple of universities, and making customized access solutions for e.g. when you have a playground with many users at different privilege levels, and subsections with their own admins, including sub-websites open to the internet. It's not simple when you're responsible nothing leaks that shouldn't leak.)
Okay, so if we believe that a ground-up design from scratch is unlikely to lead to an actual project solving the underlying problems OP (Sherlock Holmes) wants to solve, what would?
Pick a language, and a compiler, you feel you can work with. It could be C, it could be Ada, it could be whatever you want. Obviously, it should have somewhat close syntax to what you prefer, but it doesn't have to be an exact match. I'll use C as the language example below for simplicity only; feel free to substitute it with something else.
Pick a problem, find languages that solve it better than C, or invent your own new solution. Trace it down to the generated machine code, and find a way to port it back to C, replacing the way C currently solves it. Apply it in real life, writing real-world code that heavily uses that modified feature. Get other people to comment on it, and maybe even test it. Find out if the replacement solution actually helps with real-world code. That often means getting an unsuspecting victim, and having them re-solve a problem using the modified feature, using only your documentation of the feature as a guide.
Keep a journal of your findings.
At some point, you find that you have enough of those new solutions to construct a completely new language. At this point, you can tweak the syntax to be more to your liking. Start writing your own compiler, but also document the language the compiler works with, precisely. As usual, something like ABNF is sufficient for syntax, but for the paradigm, the approach, I suggest writing additional documentation explaining your earlier findings, and the solution approach. Small examples are gold here. The idea is that other people, reading this additional documentation, can see how you thought, so they can orient themselves to best use the new language.
Theory is nice, but practical reality always trumps theory. Just because the ABNF of a language looks nice, doesn't mean it is an effective language. As soon as you can compile running native binaries, start creating actual utilities – sort, grep, bc for example –, and look at the machine code the compiler generates. Just because the code is nice and the abstractions just perfect, does not mean they are fit for generating machine code. Compare the machine code to what the original language and other languages produce, when optimizations are disabled (for a more sensible comparison).
During this process, do feel free to occasionally branch into designing your language from scratch. If you keep tabs on your designs as your understanding evolves, you'll understand viscerally what the 'amount of design choices' I wrote above really means. It can be overwhelming, if you think of it, but going at it systematically, piece by piece, with each design choice having an explanation/justification in your journal and/or documentation, it can be done, and done better than what we have now.
Finally: I for one prefer passionate, honest, detailed posts over dispassionate politically correct smooth-talk.
When talking about low level programming languages –– which is what I understand 'a hardware "oriented" programming language' to mean ––, C is just the one with the best proven track record decades long. It isn't that great, it's just the 'benchmark' one for others, due to its widespread use and role in systems programming and embedded development.
For examples of nearly bug-free programs written in C in the systems programming domain, go check out D. J. Bernstein's djbdns, qmail, daemontools, cdb. This is the guy behind Curve25519, having released it in 2005.
Like it, dislike it, doesn't matter, C is just a tool. But as a tool, its features and track record are significant. So are its deficiencies, and that means any real effort to do better is valuable.
In comparison, C# is a managed language. .NET Micro requires at least 256k of RAM. .NET nanoFramework requires at least 64k of RAM, and runs on Cortex-M and RISC-V (ESP32-C3) cores. So, perhaps suitable for medium to large embedded devices, but decidedly unsuitable for small ARMs and anything less than 32-bit architecures.
Ada can be used to program AVR 8-bit microcontrollers (see AVR-Ada), but it is still relatively little used. One possible reason is that while GCC GNAT is GPL3+ licensed with a runtime library exception, AdaCore sells GNAT Pro, and the FSF/GCC GNAT is seen as "inferior", with the "proper" version being the sole product of a commercial company. (Or maybe that's just me.)
No, that's not it at all. Not pointless, more like bass-ackwards. We want the results too, we just have seen your approach before leading to nowhere. We're trying to steer you to not repeat that, but actually produce something interesting.
If you start a language design from scratch, you must understand the amount of design choices already made for existing languages. The ones in languages that have survived use in anger, are the ones where the choices support a programming paradigm the users find intuitive and effective.
Why did DiTBHo not start from scratch, and instead pared down C to a subset with some changes and additions, to arrive at their my-C, designed for strictly controlled and enforced embedded use cases? Because they needed a tool fit for a purpose, and it was a straightforward way to achieve it. Results matter.
Okay, so if we believe that a ground-up design from scratch is unlikely to lead to an actual project solving the underlying problems OP (Sherlock Holmes) wants to solve, what would?
Theory is nice, but practical reality always trumps theory. Just because the ABNF of a language looks nice, doesn't mean it is an effective language. As soon as you can compile running native binaries, start creating actual utilities – sort, grep, bc for example –, and look at the machine code the compiler generates. Just because the code is nice and the abstractions just perfect, does not mean they are fit for generating machine code. Compare the machine code to what the original language and other languages produce, when optimizations are disabled (for a more sensible comparison).
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
The first challenge I ran into with Rust was getting my firmware to run on hardware varying from 4-button dev-kit PCBs to the left/right halves of a wireless split to a single Atreus.
Varying the features of firmware at compile-time is known as “conditional compilation”. (It needs to be done at compile-time rather than run-time because microcontrollers have limited program space, roughly 10–100kB in my case.). Rust’s solution to this problem is “features”.
Conceptually Zig’s inline for is solving the same problem that Rust’s syntax macro solves (generating type-specific code at compile-time), but without the side quest of learning a lil’ pattern matching/expansion language. Rust has many language features and they’re all largely disjoint from each other, so knowing some doesn’t help me guess the others.
Conversely, this “consistency” principle also explains why I had such an easy time picking up Zig — it absolutely excels in this department. Not only are there many fewer features to learn in the first place, they seem to all fit together nicely: The comptime and inline for keywords, for example, allowed me to leverage at compile-time all the looping, conditions, arithmetic, and control flow I wanted using the syntax and semantics I’d already learned — Zig!
If extensibility is important, then domain specific libraries are the way to go.
Your initial questions were about far more than grammar, and therefore potentially more interesting. That's why people contributed.
You have consistently failed to understand that the main topic of importance is the runtime behaviour (in a broad sense) of something expressed in a language. Anything that improves the runtime behaviour is potentially important.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?I'm saying that they are not at all important to microcontroller or embedded developers at all.
I am saying that you are trying to solve a problem by daydreaming about irrelevant stuff, when you could be working on making things actually better.
Are you sure you're not a Windows C# enthusiast who dreams of creating a C#-like language that makes all embedded devices look like Windows or at least .Net runtime –– because "Windows is basically the entire world, after all", and being adulated for it? You definitely sound like one. I've met many, and all have either crashed or burned, or keep making unreliable unsecure shit and getting by with their social skills alone.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
How did you establish that? what evidence supports this claim?
Here's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:QuoteThe first challenge I ran into with Rust was getting my firmware to run on hardware varying from 4-button dev-kit PCBs to the left/right halves of a wireless split to a single Atreus.
Varying the features of firmware at compile-time is known as “conditional compilation”. (It needs to be done at compile-time rather than run-time because microcontrollers have limited program space, roughly 10–100kB in my case.). Rust’s solution to this problem is “features”.QuoteConceptually Zig’s inline for is solving the same problem that Rust’s syntax macro solves (generating type-specific code at compile-time), but without the side quest of learning a lil’ pattern matching/expansion language. Rust has many language features and they’re all largely disjoint from each other, so knowing some doesn’t help me guess the others.QuoteConversely, this “consistency” principle also explains why I had such an easy time picking up Zig — it absolutely excels in this department. Not only are there many fewer features to learn in the first place, they seem to all fit together nicely: The comptime and inline for keywords, for example, allowed me to leverage at compile-time all the looping, conditions, arithmetic, and control flow I wanted using the syntax and semantics I’d already learned — Zig!
If extensibility is important, then domain specific libraries are the way to go.
They are certainly a way to go, I'm not advocating against libraries! A language that had no "volatile" specifier could be fine if it relied on a library to manipulate such data, but does that mean you therefore disapprove of C having a "volatile" keyword? Which do you think is preferable, a keyword for it or a librart?
Your initial questions were about far more than grammar, and therefore potentially more interesting. That's why people contributed.
You have consistently failed to understand that the main topic of importance is the runtime behaviour (in a broad sense) of something expressed in a language. Anything that improves the runtime behaviour is potentially important.
Please do not venture to speculate what you think I understand or do not understand, that's a disparaging remark, an ad-hominem.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
How did you establish that? what evidence supports this claim?
Observation over the decades. Basically MCU developers can swap grammars relatively easily and quickly. What trips them up is the behaviour, i.e. how the concepts expressed in a grammar (any grammar!) map onto the MCU's runtime behaviour.QuoteHere's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:QuoteThe first challenge I ran into with Rust was getting my firmware to run on hardware varying from 4-button dev-kit PCBs to the left/right halves of a wireless split to a single Atreus.
Varying the features of firmware at compile-time is known as “conditional compilation”. (It needs to be done at compile-time rather than run-time because microcontrollers have limited program space, roughly 10–100kB in my case.). Rust’s solution to this problem is “features”.QuoteConceptually Zig’s inline for is solving the same problem that Rust’s syntax macro solves (generating type-specific code at compile-time), but without the side quest of learning a lil’ pattern matching/expansion language. Rust has many language features and they’re all largely disjoint from each other, so knowing some doesn’t help me guess the others.QuoteConversely, this “consistency” principle also explains why I had such an easy time picking up Zig — it absolutely excels in this department. Not only are there many fewer features to learn in the first place, they seem to all fit together nicely: The comptime and inline for keywords, for example, allowed me to leverage at compile-time all the looping, conditions, arithmetic, and control flow I wanted using the syntax and semantics I’d already learned — Zig!
One data point (and a very arguable data point at that) does not constitute a convincing argument.
Quote
If extensibility is important, then domain specific libraries are the way to go.
They are certainly a way to go, I'm not advocating against libraries! A language that had no "volatile" specifier could be fine if it relied on a library to manipulate such data, but does that mean you therefore disapprove of C having a "volatile" keyword? Which do you think is preferable, a keyword for it or a librart?
The significance of a keyword is not that it is one symbol (of many symbols) in a grammar. The significance is how it is mapped onto runtime behaviour. The objective is to ensure that multiple sources that cause data mutation (threads, hardware registers, interrupts etc) have defined predictable useful behaviour. All that is necessary is that primitives (language and hardware) exist to express the necessary concepts.
There are, of course, several such low-level mechanisms described in the literature over the decades, and different languages include different low-level mechanisms. Those low-level mechanisms are usually "wrapped" into several more useful high-level conceptual mechanisms in the form of libraries expressing useful "Design Patterns", e.g. Posix threads or Doug Lea's Java Concurrency Library.
"Naked" use of the primitives (rather than well designed and debugged) libraries of design patterns is a frequent source of subtle unrepeatable errors.
Notice that the language grammar is completely irrelevant in that respect; the runtime behaviour is what's important.
QuoteYour initial questions were about far more than grammar, and therefore potentially more interesting. That's why people contributed.
You have consistently failed to understand that the main topic of importance is the runtime behaviour (in a broad sense) of something expressed in a language. Anything that improves the runtime behaviour is potentially important.
Please do not venture to speculate what you think I understand or do not understand, that's a disparaging remark, an ad-hominem.
I call 'em as I see 'em. Others are making similar observations.
I don't think you understand what an ad-hominem argument is and isn't.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
How did you establish that? what evidence supports this claim?
Observation over the decades. Basically MCU developers can swap grammars relatively easily and quickly. What trips them up is the behaviour, i.e. how the concepts expressed in a grammar (any grammar!) map onto the MCU's runtime behaviour.QuoteHere's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:QuoteThe first challenge I ran into with Rust was getting my firmware to run on hardware varying from 4-button dev-kit PCBs to the left/right halves of a wireless split to a single Atreus.
Varying the features of firmware at compile-time is known as “conditional compilation”. (It needs to be done at compile-time rather than run-time because microcontrollers have limited program space, roughly 10–100kB in my case.). Rust’s solution to this problem is “features”.QuoteConceptually Zig’s inline for is solving the same problem that Rust’s syntax macro solves (generating type-specific code at compile-time), but without the side quest of learning a lil’ pattern matching/expansion language. Rust has many language features and they’re all largely disjoint from each other, so knowing some doesn’t help me guess the others.QuoteConversely, this “consistency” principle also explains why I had such an easy time picking up Zig — it absolutely excels in this department. Not only are there many fewer features to learn in the first place, they seem to all fit together nicely: The comptime and inline for keywords, for example, allowed me to leverage at compile-time all the looping, conditions, arithmetic, and control flow I wanted using the syntax and semantics I’d already learned — Zig!
One data point (and a very arguable data point at that) does not constitute a convincing argument.
I agree, but is so much better than no data points - if you get my drift. Your observations are fine, but all observations are interpretations of data, you cannot exclude your own biases and prejudices.Quote
If extensibility is important, then domain specific libraries are the way to go.
They are certainly a way to go, I'm not advocating against libraries! A language that had no "volatile" specifier could be fine if it relied on a library to manipulate such data, but does that mean you therefore disapprove of C having a "volatile" keyword? Which do you think is preferable, a keyword for it or a librart?
The significance of a keyword is not that it is one symbol (of many symbols) in a grammar. The significance is how it is mapped onto runtime behaviour. The objective is to ensure that multiple sources that cause data mutation (threads, hardware registers, interrupts etc) have defined predictable useful behaviour. All that is necessary is that primitives (language and hardware) exist to express the necessary concepts.
There are, of course, several such low-level mechanisms described in the literature over the decades, and different languages include different low-level mechanisms. Those low-level mechanisms are usually "wrapped" into several more useful high-level conceptual mechanisms in the form of libraries expressing useful "Design Patterns", e.g. Posix threads or Doug Lea's Java Concurrency Library.
"Naked" use of the primitives (rather than well designed and debugged) libraries of design patterns is a frequent source of subtle unrepeatable errors.
Notice that the language grammar is completely irrelevant in that respect; the runtime behaviour is what's important.
Yes you keep saying that but you never answered the question - if libraries are the "way to go" do you think C should not have a "volatile" keyword and instead rely on a library? if you don't understand the question then say so, if you do then is your answer "yes" or "no" or "I don't know"?
Even for the people who just want Carry, I highly doubt they are prepared for the different implementations of the carry flag with respect to subtraction and compare.
And it's simply not hard to express what you ACTUALLY WANT in portable C anyway. The compiler will automatically use the flags where available.
Bignums aren't easy in portable C :)
Well, yes they are, as just demonstrated.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
How did you establish that? what evidence supports this claim?
Observation over the decades. Basically MCU developers can swap grammars relatively easily and quickly. What trips them up is the behaviour, i.e. how the concepts expressed in a grammar (any grammar!) map onto the MCU's runtime behaviour.QuoteHere's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:QuoteThe first challenge I ran into with Rust was getting my firmware to run on hardware varying from 4-button dev-kit PCBs to the left/right halves of a wireless split to a single Atreus.
Varying the features of firmware at compile-time is known as “conditional compilation”. (It needs to be done at compile-time rather than run-time because microcontrollers have limited program space, roughly 10–100kB in my case.). Rust’s solution to this problem is “features”.QuoteConceptually Zig’s inline for is solving the same problem that Rust’s syntax macro solves (generating type-specific code at compile-time), but without the side quest of learning a lil’ pattern matching/expansion language. Rust has many language features and they’re all largely disjoint from each other, so knowing some doesn’t help me guess the others.QuoteConversely, this “consistency” principle also explains why I had such an easy time picking up Zig — it absolutely excels in this department. Not only are there many fewer features to learn in the first place, they seem to all fit together nicely: The comptime and inline for keywords, for example, allowed me to leverage at compile-time all the looping, conditions, arithmetic, and control flow I wanted using the syntax and semantics I’d already learned — Zig!
One data point (and a very arguable data point at that) does not constitute a convincing argument.
I agree, but is so much better than no data points - if you get my drift. Your observations are fine, but all observations are interpretations of data, you cannot exclude your own biases and prejudices.Quote
If extensibility is important, then domain specific libraries are the way to go.
They are certainly a way to go, I'm not advocating against libraries! A language that had no "volatile" specifier could be fine if it relied on a library to manipulate such data, but does that mean you therefore disapprove of C having a "volatile" keyword? Which do you think is preferable, a keyword for it or a librart?
The significance of a keyword is not that it is one symbol (of many symbols) in a grammar. The significance is how it is mapped onto runtime behaviour. The objective is to ensure that multiple sources that cause data mutation (threads, hardware registers, interrupts etc) have defined predictable useful behaviour. All that is necessary is that primitives (language and hardware) exist to express the necessary concepts.
There are, of course, several such low-level mechanisms described in the literature over the decades, and different languages include different low-level mechanisms. Those low-level mechanisms are usually "wrapped" into several more useful high-level conceptual mechanisms in the form of libraries expressing useful "Design Patterns", e.g. Posix threads or Doug Lea's Java Concurrency Library.
"Naked" use of the primitives (rather than well designed and debugged) libraries of design patterns is a frequent source of subtle unrepeatable errors.
Notice that the language grammar is completely irrelevant in that respect; the runtime behaviour is what's important.
Yes you keep saying that but you never answered the question - if libraries are the "way to go" do you think C should not have a "volatile" keyword and instead rely on a library? if you don't understand the question then say so, if you do then is your answer "yes" or "no" or "I don't know"?
Sigh. That's a false dichotomy.
Not only isn't your question the right question, it isn't even the wrong question. It is, however, a reflection of the point I've been trying (and failing) to get you to understand: the difference between syntax/language and semantics/meaning/behaviour. Most people here care deeply about the latter, but don't care about the former.
The behaviour I mentioned above is needed, the keyword isn't. Whatever syntax and primitives are used, they will usually be wrapped up in a library.
Even for the people who just want Carry, I highly doubt they are prepared for the different implementations of the carry flag with respect to subtraction and compare.
And it's simply not hard to express what you ACTUALLY WANT in portable C anyway. The compiler will automatically use the flags where available.
Bignums aren't easy in portable C :)
Well, yes they are, as just demonstrated.
Yep they are indeed.
I have implemented some kind of arbitrary precision library. It's able to do with various base integer widths without a problem. I've admittedly used some of the GCC's builtins to speed things up (such as the 'compute with overflow' kind of builtins), which themselves are reasonably portable if you stick to GCC, but I could have perfectly done without them and make the code 100% standard C code.
A grammar is necessary (not sufficient of course), unavoidable in fact so we need a grammar and I'm of the opinion that a grammar based on PL/I subset G is extremely attractive for reasons I've already articulated. A criticism I have of many, many newer languages like Rust, like Hare like Zig like Go like Swift even C# is that they are based on the C grammar and therefore do - unfortunately - stand to repeat some of the sins of the past.I see grammar as an arbitrary choice, related more to what the designer considers "pretty" than anything else.
I had implemented a 32 bit floating point package on a 6800 a decade earlier, in a medium level language. Without checking, I would have used the carry flag, which doesn't exist in C
I had implemented a 32 bit floating point package on a 6800 a decade earlier, in a medium level language. Without checking, I would have used the carry flag, which doesn't exist in C
This caught my eye "carry flag" are you of the opinion this could be useful? if it had existed in C?
Yes you keep saying that but you never answered the question - if libraries are the "way to go" do you think C should not have a "volatile" keyword and instead rely on a library? if you don't understand the question then say so, if you do then is your answer "yes" or "no" or "I don't know"?
Sigh. That's a false dichotomy.
Not only isn't your question the right question, it isn't even the wrong question. It is, however, a reflection of the point I've been trying (and failing) to get you to understand: the difference between syntax/language and semantics/meaning/behaviour. Most people here care deeply about the latter, but don't care about the former.
The behaviour I mentioned above is needed, the keyword isn't. Whatever syntax and primitives are used, they will usually be wrapped up in a library.
Very well I'll tell you, the answer is in fact "no" because C with a volatile keyword is preferable to a library method invocation both syntactically and semantically and for performance reasons too.
Now, what are examples of these semantic, behavioral concepts you "care deeply about" I'm as interested in this as I am grammar, but was focusing on grammar initially, if you want to start discussing behaviors then fine.
One cannot have a language without a grammar so one must - somehow - identify a grammar to use, it is an essential part of a programming language, you seem to be saying it is irrelevant, well if that were true use assembler or perhaps COBOL or RPG?
Finally as I just pointed out but you seem to have missed, libraries do not grow on trees, they are written and they are written using a language, you cannot wave away the language issue by replacing it with a library, all that does is move the code, it doesn't eliminate it.
A grammar is necessary (not sufficient of course), unavoidable in fact so we need a grammar and I'm of the opinion that a grammar based on PL/I subset G is extremely attractive for reasons I've already articulated. A criticism I have of many, many newer languages like Rust, like Hare like Zig like Go like Swift even C# is that they are based on the C grammar and therefore do - unfortunately - stand to repeat some of the sins of the past.
I see grammar as an arbitrary choice, related more to what the designer considers "pretty" than anything else.
It is the language paradigm (generic approach to problem solving), and concepts, that matter.
If you start with a grammar, you are basically starting with the statement "This is the way all possible concepts in this programming language shall be expressed."
If you start at the conceptual level, you sketch out the features of the language, and can then choose a grammar that best suits the needs.
In my opinion, the only reason to start from the grammar upwards is if you 1) believe you already know everything you need to know about the language being designed, and/or 2) how it looks is of paramount importance to you. (These are the two reasons I've seen in the wild, that is.)
Let's use my own gripes with C as a starting point, and examine how the above affects the design process and the result.
Because of embedded uses, I want to allow pointers to specific addresses and to individual objects, but I do not want them to be extensible to arrays, except via explicit constructs that also specify the size/range of the array.
I do not want to force the ABI to pass arrays as (origin, length) or (origin, step, length) tuples, because my aim is to be able to prove that all accesses are valid using static code analysis tools. If a cosmic ray flips a bit and that causes a crash because of lack of runtime bounds checking, I'm okay with that. (This, of course, is a design choice, and by no means the only one possible!)
I also like being able to redefine basic operators for custom object types. I don't want to go whole-hog object-oriented, making the operator overloading object-specific; I'm fine with the operator overloading being purely based on static types only. (I am aiming at small to medium-sized embedded devices, and the cost of the indirection via object pointers is too much for this niche, in my opinion. Another design choice.)
I now cobble together something that seems to compile a small snippet of source code to pseudo-assembly.
Uh-oh, problem. Because I do not force ABIs to explicitly pass a parameter describing the array length, I need a way to explicitly extract the origin and the length of an array. I could use built-in functions or address and length operators. Except that if I have already decided on a grammar, my choice is dictated by that grammar. Oops.
In real life, both options have their upsides and downsides. When the length is needed in a function where the array was passed as a parameter, we really do need to pass the length also. There are several options for this particular design choice, but they all boil down to the fact that in a function specification, we need a way to specify that a parameter is the length of a specific array also passed as a parameter. (With this information, my compiler can tell at compile-time whether the length operator or built-in function is allowed.) This significantly affects the grammar, of course!
If my grammar was locked in already, I'd have to cobble together a less than optimal workaround.
Even though I have piles of experience myself, I know I am not aware of all the features and details beforehand. But I also know that I can construct the grammar as I go along, and collect and implement all the features of the new programming language. Having the grammar be defined by the features, instead of vice versa, gives me the most leeway.
For example, if I wanted this language to be mostly compatible with C otherwise, I would make the address-of and length-of operators, so that those porting code would need to pay attention to every case where a pointer is used as an array or vice versa. C already has an address-of operator &, but it might be confusing, and make the static code analysis more difficult. Instead, I might choose say @ and #, or origin and length (keyword operators, like sizeof). But I do not know yet; I would experiment with it in practice –– on unsuspecting victims, preferably –– to see if they grasp the concept intuitively, and therefore would be likely to apply this to write memory-safer code. Locking in the grammar up front makes such experiments irrelevant; the decision has been made already.
Thus, the obvious conclusion from going at it grammar-first is that it is grandiose, futile, waste of time, or all three.
Do not forget that others are not commenting about you personally, they are commenting your approach and what you describe in your output.
This is also why I use a pseudonym, instead of my real name: it helps me remember that any negative feedback I get is based on my output, my communications, and not my person. I can change my communication style somewhat –– although verbosity seems to be a fixed feature for me ––, but there is no reason to think my person is under attack, so although discussion can get very heated at times, there is no reason to consider it as between persons: it is just a heated discussion. In a different concurrent discussion with the same members, I can be (and have been), quite calm and relaxed –– again, because it is not about persons, the heat is only about the subject matter. I think sometimes a little heat is good, because it shows that others care, and because it (at least for me) causes one to reconsider their own output, to see if the heat is on track, or just a mistargeted flamethrower. Useful, in other words.
I don't think its very easy to devise a programming language grammar that is 100% free from the influence of other previous languages. There's no such thing as a totally, brand new, fresh grammar, or it's very very rare. APL could be considered an example of that though, I suppose.
Consider too C#, Java, C++, Swift, Objective-C, Perl, PHP, JavaScript, Go, Rust and more, all are regarded as being derived from the C grammar. So all of these must have selected - up front - C as the basis for their grammar.
Someone else mentioned that "the carry flag isn't available in C" well this is the kind if thing I'd like to hear more about, would it, could it, be helpful to expose such a concept in the language?
I don't think its very easy to devise a programming language grammar that is 100% free from the influence of other previous languages. There's no such thing as a totally, brand new, fresh grammar, or it's very very rare. APL could be considered an example of that though, I suppose.
I've used LISP, Prolog, Forth, Smalltalk, Algol-60, various hardware definition languages. They all have radically different syntaxes.QuoteConsider too C#, Java, C++, Swift, Objective-C, Perl, PHP, JavaScript, Go, Rust and more, all are regarded as being derived from the C grammar. So all of these must have selected - up front - C as the basis for their grammar.
And there you unwittingly demonstrate our point. The interesting and important differences between those languages has nothing to do with grammar.
Someone else mentioned that "the carry flag isn't available in C" well this is the kind if thing I'd like to hear more about, would it, could it, be helpful to expose such a concept in the language?
Start by understanding the concepts in https://www.jameswhanlon.com/the-xc-programming-language.html (https://www.jameswhanlon.com/the-xc-programming-language.html) amd https://www.xmos.ai/download/XMOS-Programming-Guide-(documentation)(F).pdf (https://www.xmos.ai/download/XMOS-Programming-Guide-(documentation)(F).pdf)
I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.
I used a naked "8-pin Arduino" in my Vetinari clock (https://entertaininghacks.wordpress.com/2015/02/23/vetinari-digital-clock/), because that was the quick way to get an undemanding result.That's exactly why ATmega32u4 is one of my favourite 8-bitters. It has native full-speed USB (12 Mbit/s, can do 1 Mbyte/sec even over USB Serial), but still is completely programmable without any HAL or similar, on bare metal, using nothing but avr-gcc (and avr-libc, if you really want), in (freestanding) C or C++.
But when making something that required very low power consumption, I simply wrote C to peek/poke the atmega328 registers. That avoided me having to work out how Arduino libraries might be frustrating me.I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.
An extremely common pattern for functions is to return a value or error. Obviously, with scalar return types, when you return a single scalar, some values must be reserved for error codes. (That can get messy, like it once did in the Linux kernel, where certain filesystem drivers could not handle reads over 231 bytes (due to widespread use of int), and returned negative values. Now, the Linux kernel limits all single read() and write() syscalls to 231 less one page automagically.) Having a separate value and status returns is sometimes more robust, even if the status return is just a single "OK"/"Error" flag.
However, SysV ABI on x86-64 (https://wiki.osdev.org/System_V_ABI#x86-64) (LP64, so ints are 32-bit, long and pointers are 64-bit) as used on Linux, macOS, FreeBSD, and others, already supports this (by using rax and rdx registers for 128-bit non-FP return values), so code like
typedef struct { long x, y } ipair;
lpair somefunc(long x, long y) { return (lpair){ .x = x, .y = y }; }
compiles to
somefunc:
mov rax, rdi
mov rdx, rsi
ret
because rdi and rsi are the first two (non-FP) parameters, and the pair of return values are returned in rax and rdx.
This suffices for my needs, although it is slightly odd-looking at first, wrapping return values into structs. It would be nicer to have syntactic sugar for this and not require struct use, but this works too.
Because this is architecture ABI, any compiler for this architecture can use this. Other ABIs, however, need to do annoying "emulation" by e.g. passing a pointer to the additional return values, similar to how larger (struct-type) result values are passed. A compiler can obviously use its own function call ABI internally, but it does have to have some kind of foreign function interface* for bindings to binary libraries (if used on hosted environments/full OSes, and not just bare metal embedded). It might be useful to use a rarely used register for the status/error return, so that it is not so often clobbered by ordinary code.
Simply put, I wasn't hankering so much for syntactic sugar, but for more sensible ABIs like SysV on x86-64. Brucehoult put me straight on this, albeit indirectly. :-+
* Part of the reason I like using Python for graphical user interfaces is that it has a very nice interface to calling native code, without needing any native code support; using pure Python code for the interface. Certain libraries, like glib's gobject introspection for C, provide introspection information (.gi or gir files) that means full interfaces can be constructed without any bespoke code (machine or Python), using the introspection information only. It also means that when the library and corresponding introspection file is updated, the new features are immediately available in Python also. Very useful.
Because native machine code is dynamically loaded for access from Python, this also provides a perfect mechanism for license separation. You can keep the user interface open source, modifiable by end users, but keep all Sekret Sauce proprietary code in your binary-only dynamic library. In my experience, this provides the optimal balance between user access and proprietary requirements when going full open source isn't viable, e.g. for commercial reasons.
if (TryGetRemoteSystemStatus(system_id, var out status))
{
// do stuff.
}
Ehh, what? :oHow did you establish that? what evidence supports this claim? Here's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
Exactly. No, I don't need unbounded parameters or return values myself; even for printf and friends I prefer a string-building approach (if you recall my old replacement for standard C thread).Yes. Multiple return values are very desirable. Returning structures can substitute, but it's notationally annoying.I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.
Most modern ABIs allow returning two word-size or one double sized value. With register based ABIs, usually in the same registers as used for the first two arguments. x86_64 is a weird exception with the first argument passed in RDI but the result in RAX.
But why can't you pass the same number of results as arguments? Function return is semantically no different to function call -- it is "calling the continuation". It would clearly be very very easy to enable this in modern ABIs that pass up to 6 or 8 arguments in registers -- it's simply relaxing a restriction. Figuring out where to put return values that overflow into RAM is harder. If the caller knows how many results are to be returned then it can allocate stack space for max(arguments, results). Support an unbounded number of return values would be a lot harder. But it's also hard to see a reason to support it. VARARGS seems like mostly a hack to support printf and friends.
Ehh, what? :oHow did you establish that? what evidence supports this claim? Here's a post by an engineer (https://kevinlynagh.com/rust-zig/) that proves you wrong, I quote:So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.
What has that post by that engineer have to do with language grammar? They're talking about conditional builds, which is solved by any toolchain that generates linkable object files by selecting which object files to compile and include in the final image.
ELF object files support per-symbol sections, regardless of the language you use, which allows the linker to select which symbols (usually functions) to include based on which symbols may be referenced. It means that only features you actually use are included in the linked target, and this is heavily used in current embedded toolchains, which basically all use ELF object files. You do not even have to deselect the features you do not need; all you need to do is make sure all you might need is available (via whatever include or import mechanism your language uses), and use the ones you want. Only the features you use, and the features they refer to, will get included in the final linked object.
The way we established "language grammar isn't important to embedded developers" is by being, and working with embedded developers, who use whatever tools fit the task at hand best. Grammar is just flavour. I've never heard of any successful developer choosing their programming language or environment based on the grammar.
I have seen many who did choose their programming language based on grammar or because it was the only language they could use, fail. I've talked with, and continuously talk to, and occasionally help, many developers, both in embedded and application development. Grammar has never been more than a flavour on top, as in "I find it ugly" or "it looks neat"; never at the core of any discussion related to solving a problem.
I'm not sure if I have talked shop in such details with the commonly suggested 1000 persons sufficient for a random opinion poll, and the selection definitely isn't random, but it is a significant number, well over a hundred at minimum. I also do not shy away from antagonistic discussions, so I often disagree with others –– often to find out the underlying reasons for their opinions, because opinions themselves aren't useful, but the reasons underlying the opinions are. (For one, opinions cannot be compared in any rational manner, but the reasons for those opinions can usually be rationally examined and compared.)
You can pass and return structures in C, which makes your life much easier. Yes the downside is that you need 1/ to define the structure first and 2/ to access its members. Sure a bit more syntax overhead. Interesting to note that very few developers ever use that feature.Things get very interesting when you start using e.g. in C
I have mixed "feelings" about returning multiple values though. A few reasons for this, but one of them being the "implicit" order. Note that this is the exact same issue with function parameters. Ada fixes it by using parameter designators, although those are optional. But at least function parameters have an identifier. Many languages handle returning multiple values using some kind of tuples - so no identifier per se associated with each value returned inside the tuple, only some guaranteed order.I do not know what kind of syntax would work best, either.
So please don't present personal anecdotal perceptions as being anything more than that, this is why you'll sometimes see "IMHO" inside some of my posts.
concentrate on my questions..
When I last looked at the problem, c1986, it had to be portable across machines, the language was much simpler and any given compiler targeted, at best, a small subset of machines.
My comment was based on erroneously ignoring the changes.since then. Mea culpa.
I had implemented a 32 bit floating point package on a 6800 a decade earlier, in a medium level language. Without checking, I would have used the carry flag, which doesn't exist in C
So please don't present personal anecdotal perceptions as being anything more than that, this is why you'll sometimes see "IMHO" inside some of my posts.
Ah, I see. When you post someones personal anecdotal perceptions, they are 'proof'.
When I describe my own experiences discussing these technical details with well over a hundred other embedded developers, they're just 'perceptions', and not indicative of anything.
You, sir, are full of shit.
I'm out. I'm not ready to try to help another electrodacus see the reality from their own preconceptions, sorry.
It would have been convenient in that case, but different processors have very different semantics for their carry flag.
For all I know there may be processors without a carry flag; I no longer bother to keep up with processor ISAs.
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
For all I know there may be processors without a carry flag; I no longer bother to keep up with processor ISAs.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
If Alpha had crossed my mind, I would have presumed it didn't, since that went to all extremes to excise everything they could. (And, IIRC, excised byte addressing - oops).
I'm not sure what the other currently interesting architecture (the Mill) does.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
also, subtract with borrow (68k) vs subtract with carry (ppc)
At least the following use subtract with carry: IBM System/360, 6502, PIC, MSP430, ARM, PowerPC
At least the following use subtract with carry: IBM System/360, 6502, PIC, MSP430, ARM, PowerPC
Don't know about the others but although the 6502 instruction is called SBC (subtract with carry) my R6500 programming manual states it is with borrow.
You have to set the carry before the first subtract to indicate no borrow.
If Alpha had crossed my mind, I would have presumed it didn't, since that went to all extremes to excise everything they could. (And, IIRC, excised byte addressing - oops).
Alpha always had byte addressing. What it doesn't have, for the first half dozen models (until 199's "EV56" 21164A), was byte (and 16 bit) load and store instructions. It wasn't a raw speed thing -- Alpha was often the fastest CPU in the world during that time, including on software doing a lot of byte operations.
The problem was multiprocessor systems and software that assumes byte accesses are atomic.
QuoteI'm not sure what the other currently interesting architecture (the Mill) does.
No globals flags, of course :-) Each value on the belt has its own metadata.
In http://millcomputing.com/topic/metadata/#post-419 (http://millcomputing.com/topic/metadata/#post-419) Ivan wrote:
"It would certainly be possible to have a carry metabit in each byte, and we considered it. After all, there is already a NaR bit in each byte. However, we could not find enough usage for such a design to justify the cost."
That was 2014. Things could have changed. I don't know.
I make the following bold claim: No clean sheet ISA designed for high performance since 1990 has had a carry flag, or global flags register.
Note 1) IBM RS/6000->PowerPC->POWER is exactly 1990. It has 8 flags registers.
Note 2) ARM Aarch64 is not a clean sheet design. While it makes quite a lot of changes from Aarch32, it needs to share an execution pipeline with 32 bit code, with both running optimally, at least for the first 10 years or so.
Around the same time I was skeptical about the approach of the HP PA-RISC successor,It was obvious from the very start of Itanium development that its strategy would make for extremely high power consumption. So, it seemed to be a strategy to split the market into mobile and non-mobile threads. That was always a very weird strategy for a company like Intel, when notebooks were a booming business.ItanicItanium. At that time any tiny change to the implementation required someone to re-hand-optimise inner loops; the compiler was supposed to solve that issue, but never quite managed it. In addition, just as CPU power consumption was becoming the limiting factor, the Itanic strategy was to waste power doing lots of speculative execution.
The other strategy exhibiting that phenomenon was the TI9900's registers being in memory. Great for fast context changes, but somewhat limiting otherwise.No architecture is general purpose. They all have a time and a place. The 9900, and the 990 mini-computers based on the same architecture, worked really well in their day. The 990 was one of the biggest selling min-computer lines, although most people have barely heard of it. It crunched a lot of number in things like geophysical survey and defence applications. Memory and circuit performance changed. Even small machines started to get cache. All the underpinings of why the 990 made sense changed, and it went away.
concentrate on my questions..
We are not here at your beck and call...
Now you need to read (not skim) the points, and do the work...
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
The only frothing is coming at me, not from me.
I see others I respect have now reached and expressed the same conclusion as I did. To their credit, they perhaps gave the benefit of the doubt for longer, though not forever.
That's exactly why ATmega32u4 is one of my favourite 8-bitters. It has native full-speed USB (12 Mbit/s, can do 1 Mbyte/sec even over USB Serial), but still is completely programmable without any HAL or similar, on bare metal, using nothing but avr-gcc (and avr-libc, if you really want), in (freestanding) C or C++.
uint8_t buffer[] = {0xac, 0xdc, 0xab, 0xba, 0xcd};
NRF_UARTE0->TXD.PTR = (uint32_t)buffer;
NRF_UARTE0->TXD.MAXCNT = sizeof buffer;
NRF_UARTE0->TASKS_STARTTX = 1;
while(!NRF_UARTE0->EVENTS_ENDTX)
;
I think the unimportance of details in grammar can be demonstrated with a simple example:
Consider the achievements of human kind. We have built pyramids, airplanes, went to the Moon and whatnot. All of this is possible thanks to human being able to communicate with others, with a natural language.
Yet, such great achievements have been made in countless of different cultures, with different languages, with very different grammars and syntax.
It's the process itself, behind the grammar and syntax, which dictates the result. A surgeon can discuss with their colleagues in English, Chinese or Finnish just fine, and as long as they know the language, they absolutely do not care, it simply does not matter to them.
Same can be said about programming languages, for example C and Ada have very different looking grammar and syntax but if you learn both, it's confusing only in the very beginning. You learn the grammar and syntax quickly.
That's why it's more important to start from what the language can do and how, then the grammar will follow.
So of course we can discuss what the language can do, go ahead I've been asking for some nine pages now! For example I recently said that I think the language should support runtime access to metadata like string capacity/lengths, arrays dimensions and bounds and so on. The C language is - IMHO - rather poor in that area, so lets address that.
#define ARRAY_LEN
unsigned char array[ARRAY_LEN] = {1, 2, 3, 4};
for(int i=0; i<ARRAY_LEN; i++)
...
uint8_t array[] = {1, 2, 3, 4}; // automagic array length!
for(int i=0; i<NUM_ELEM(array); i++) // despite being automagic, length is still known, at compile time! Googling the NUM_ELEM helper macro is left as an excercise for reader. Add to utils.h or something
...
Around the same time I was skeptical about the approach of the HP PA-RISC successor,It was obvious from the very start of Itanium development that its strategy would make for extremely high power consumption. So, it seemed to be a strategy to split the market into mobile and non-mobile threads. That was always a very weird strategy for a company like Intel, when notebooks were a booming business.ItanicItanium. At that time any tiny change to the implementation required someone to re-hand-optimise inner loops; the compiler was supposed to solve that issue, but never quite managed it. In addition, just as CPU power consumption was becoming the limiting factor, the Itanic strategy was to waste power doing lots of speculative execution.
I've been pondering the idea of attempting to create a new (compiled) programming language specifically designed for hardware programming... tldr...leave the job to computer scientists and linguists, or at least, go discuss with them. what you are trying to do is like a very experienced engineer who want to lay a new theoritical physics in quantum mechanics. you may ended up reinventing the wheel or may not know what you are dealing with.. if you have to ask here, you are not the man trust me, i'm sorry to break the mood.. i'm not the man, nor most of us here. so rather than keep heated arguing endlessly, like a bunch of experienced engineers discussing about quantum mechanics that they have no clue about... find some reading materials about what a "computer programming language" is all about.. its a classical knowledge nothing new, C/C++ language was designed to be as low level as it can get while maintaining human readable syntax and avoid coupling to certain machine architecture, but generic and compatible enough for all. if you think you can do better, show your working proof. just a blueprint or "list of ideas" will not be enough. try do it actually and you may find the actual barrier in the middle of your journey. just "list of ideas" is easy to brag about. if you have to ask me what features i want in a programming language? i would say none! C/C++ provides me all i need to convert the semantic of procedural processes into machine readable (actual working) processes, through subroutines, definitions etc. if you think you have struggled in embedded system, i doubt your proficiency as a software developer, esp if you cannot differentiate between what a "language" and "compiler" and "machine dependent codes" is? you can make a parser or a compiler not necessarily you can make a new "language". cheers, ymmv.
This is not as crazy as it might at first appear, I'm a very experienced software developer
So of course we can discuss what the language can do, go ahead I've been asking for some nine pages now! For example I recently said that I think the language should support runtime access to metadata like string capacity/lengths, arrays dimensions and bounds and so on. The C language is - IMHO - rather poor in that area, so lets address that.
Accessing implementation internals like string capacity/length, but isn't that exactly where C is the best tool you have? Higher level languages hide all such implementation details in their string classes (that's the whole point). In C, such string type does not exist, you have to create your own, which also means you have access to everything you want, and exactly as you want. More work, but this is why embedded developers with limited resources prefer C.
Arrays are indeed a weak point in C, being too primitive and requiring you to babysit implementation details, but if you did actually read any of this thread, it has been discussed in numerous replies above, mostly by Mr. N. Animal. No need to complain about this not being discussed.
On the other hand, people often write crappy code like this, as taught in classes:Code: [Select]#define ARRAY_LEN
unsigned char array[ARRAY_LEN] = {1, 2, 3, 4};
for(int i=0; i<ARRAY_LEN; i++)
...
When C is much more capable:Code: [Select]uint8_t array[] = {1, 2, 3, 4}; // automagic array length!
for(int i=0; i<NUM_ELEM(array); i++) // despite being automagic, length is still known, at compile time! Googling the NUM_ELEM helper macro is left as an excercise for reader. Add to utils.h or something
...
Of course still quite primitive, but... We manage! And those who complain, often do not even know what we have available. As I said earlier, use C to its fullest, and it's not as crappy as some lazy examples from 1980's seem like.
Around the same time I was skeptical about the approach of the HP PA-RISC successor,It was obvious from the very start of Itanium development that its strategy would make for extremely high power consumption. So, it seemed to be a strategy to split the market into mobile and non-mobile threads. That was always a very weird strategy for a company like Intel, when notebooks were a booming business.ItanicItanium. At that time any tiny change to the implementation required someone to re-hand-optimise inner loops; the compiler was supposed to solve that issue, but never quite managed it. In addition, just as CPU power consumption was becoming the limiting factor, the Itanic strategy was to waste power doing lots of speculative execution.
Intel did not design the Itanic architecture. HP designed the Itanic.
The objective was simple. In 1989 it was known that HP PA-RISC would run out of steam in a decade's time. When that became significant to customers, they would look for alternatives. What became the Itanic was simply a means of keeping customers buying HP PA-RISC machines. By the mid to late 90s that was the server market.
I've been pondering the idea of attempting to create a new (compiled) programming language specifically designed for hardware programming... tldr...leave the job to computer scientists and linguists, or at least, go discuss with them. what you are trying to do is like a very experienced engineer who want to lay a new theoritical physics in quantum mechanics. you may ended up reinventing the wheel or may not know what you are dealing with.. if you have to ask here, you are not the man trust me, i'm sorry to break the mood.. i'm not the man, nor most of us here. so rather than keep heated arguing endlessly, like a bunch of experienced engineers discussing about quantum mechanics that they have no clue about... find some reading materials about what a "computer programming language" is all about.. its a classical knowledge nothing new, C/C++ language was designed to be as low level as it can get while maintaining human readable syntax and avoid coupling to certain machine architecture, but generic and compatible enough for all. if you think you can do better, show your working proof. just a blueprint or "list of ideas" will not be enough. try do it actually and you may find the actual barrier in the middle of your journey. just "list of ideas" is easy to brag about. if you have to ask me what features i want in a programming language? i would say none! C/C++ provides me all i need to convert the semantic of procedural processes into machine readable (actual working) processes, through subroutines, definitions etc. if you think you have struggled in embedded system, i doubt your proficiency as a software developer, esp if you cannot differentiate between what a "language" and "compiler" and "machine dependent codes" is? you can make a parser or a compiler not necessarily you can make a new "language". cheers, ymmv.
This is not as crazy as it might at first appear, I'm a very experienced software developer
Many high level languages actually let you find the current and maximum lengths of strings or the rank and bound of arrays at runtime. C does not, sure you can write code to somehow create abstractions that do that if you really want, but that's really what languages are for, to reduce the need for such drudgery.at what cost? as a language designer, you should already have the answer in your belt. btw, you can start designing one "highly managed" language if you want to, you can start a list of mostly used features and we would love to try... if what you have in mind is that (highly managed language), looking at C is a bad start. start looking at other highly managed language such as maybe Python? or Rust? or whatever, i dont know, i'm not a proficient "all languages" programmer... C is not a managed language. but most people use it. its like automatic car vs manual car. both will have market. i choose manual car... lets see if one day manual car extinct. C#.. anybody?
Many high level languages actually let you find the current and maximum lengths of strings or the rank and bound of arrays at runtime. C does not, sure you can write code to somehow create abstractions that do that if you really want, but that's really what languages are for, to reduce the need for such drudgery.at what cost? as a language designer, you should already have the answer in your belt.
btw, you can start designing one "highly managed" language if you want to, you can start a list of mostly used features and we would love to try... if what you have in mind is that (highly managed language), looking at C is a bad start. start looking at other highly managed language such as maybe Python? or Rust? or whatever, i dont know, i'm not a proficient "all languages" programmer... C is not a managed language. but most people use it. its like automatic car vs manual car. both will have market. i choose manual car... lets see if one day manual car extinct.
func get_fastest_motors_speed (X) float(binary(128)) optimize(speed) determinate
{
arg X binary(16);
return (something);
}
Well you need to define "cost" first and you haven't."resources" cost, mcu storage space and RAM to store all those drudgery avoidance codes. your language may not run very well in atTiny mcu, or asks for larger ARM processor etc. its inevitable... ;) this will open to endless debate. so just make one and put it into the ring, lets see who takes the bait.
Well you need to define "cost" first and you haven't."resources" cost, mcu storage space and RAM to store all those drudgery avoidance codes. your language may not run very well in atTiny mcu, or asks for larger ARM processor etc. its inevitable... ;) this will open to endless debate. so just make one and put it into the ring, lets see who takes the bait.
dcl usernames(1024) string(64); // array of 1024 strings - total metadata is 2 bytes in this specific example.
func parse_text (text) bool
{
arg text string(*);
L = length(text); // Get the capacity of this fixed length string
}
dcl usernames(1024) string(64) opaque; // array of 1024 strings - no string metadata
dcl gradenames(1024) opaque string(64) opaque; // array of 1024 strings - no string metadata no array metadata
/* or */
dcl usernames(1024) string(64,opaque); // array of 1024 strings - no string metadata
dcl gradenames(1024,opaque) string(64,opaque); // array of 1024 strings - no string metadata no array metadata
The Itanium's basic design originated at HP, and HPs own silicon designers did much or all of the design for the first silicon. However, Intel bought into this program, not just as a silicon fabricator, but as their core performance strategy going forwards. It seems they were so eager for something completely proprietary to them as their step to 64 bit systems, they forgot all the lessons the industry had learned up to that point - compatibility is king, software moves slowly, so make sure you can run the same software in the maximum possible number of places, old code needs to run well on new hardware, etc. The last point they learned well, when the brilliant Pentium Pro design bombed in the market, because it ran old 16 bot Microsoft code too slowly. Having been burned badly by that, just a few years later they did the same thing, putting a half baked x86 mode into the Itanium.Around the same time I was skeptical about the approach of the HP PA-RISC successor,It was obvious from the very start of Itanium development that its strategy would make for extremely high power consumption. So, it seemed to be a strategy to split the market into mobile and non-mobile threads. That was always a very weird strategy for a company like Intel, when notebooks were a booming business.ItanicItanium. At that time any tiny change to the implementation required someone to re-hand-optimise inner loops; the compiler was supposed to solve that issue, but never quite managed it. In addition, just as CPU power consumption was becoming the limiting factor, the Itanic strategy was to waste power doing lots of speculative execution.
Intel did not design the Itanic architecture. HP designed the Itanic.
The objective was simple. In 1989 it was known that HP PA-RISC would run out of steam in a decade's time. When that became significant to customers, they would look for alternatives. What became the Itanic was simply a means of keeping customers buying HP PA-RISC machines. By the mid to late 90s that was the server market.
Explicit support for fixed-point numbers is provided by a few computer languages, notably PL/I, COBOL, Ada, JOVIAL, and Coral 66. They provide fixed-point data types, with a binary or decimal scaling factor. The compiler automatically generates code to do the appropriate scaling conversions when doing operations on these data-types, when reading or writing variables, or when converting the values to other data types such as floating-point.
Moreover, in 2008 the International Standards Organization (ISO) issued a proposal to extend the C programming language with fixed-point data types, for the benefit of programs running on embedded processors.[3] Also, the GNU Compiler Collection (GCC) has back-end support for fixed-point.[4][5]
Digital Signal Processors have traditionally supported fixed-point arithmetic in hardware. But more recently, many DSP-enhanced RISC processors are starting to support fixed-point data types as part of their native instruction set. When the precision requirements of the application can be met with fixed-point arithmetic, then this is preferred since it can be smaller and more efficient than floating-point hardware. DSP algorithms often represent the data samples and the coefficients used in the computation as fractional numbers (between -1 and +1) to avoid magnitude growth of a multiplication product. Fractional data type, where there are zero integer bits, is a subset of the more general fixed-point data type.
The fixed-point types are
short _Fract,
_Fract,
long _Fract,
long long _Fract,
unsigned short _Fract,
unsigned _Fract,
unsigned long _Fract,
unsigned long long _Fract,
_Sat short _Fract,
_Sat _Fract,
_Sat long _Fract,
_Sat long long _Fract,
_Sat unsigned short _Fract,
_Sat unsigned _Fract,
_Sat unsigned long _Fract,
_Sat unsigned long long _Fract,
short _Accum,
_Accum,
long _Accum,
long long _Accum,
unsigned short _Accum,
unsigned _Accum,
unsigned long _Accum,
unsigned long long _Accum,
_Sat short _Accum,
_Sat _Accum,
_Sat long _Accum,
_Sat long long _Accum,
_Sat unsigned short _Accum,
_Sat unsigned _Accum,
_Sat unsigned long _Accum,
_Sat unsigned long long _Accum.
I think its time the C standard flipped things around, and started with length specific types, and derived bland vague names, like int and short int, from the specific ones - mostly for backwards compatibility and places where the size of the variable is essentially irrelevant, like a small loop counter.QuoteThe fixed-point types are
short _Fract,
_Fract,
long _Fract,
long long _Fract,
unsigned short _Fract,
unsigned _Fract,
unsigned long _Fract,
unsigned long long _Fract,
_Sat short _Fract,
_Sat _Fract,
_Sat long _Fract,
_Sat long long _Fract,
_Sat unsigned short _Fract,
_Sat unsigned _Fract,
_Sat unsigned long _Fract,
_Sat unsigned long long _Fract,
short _Accum,
_Accum,
long _Accum,
long long _Accum,
unsigned short _Accum,
unsigned _Accum,
unsigned long _Accum,
unsigned long long _Accum,
_Sat short _Accum,
_Sat _Accum,
_Sat long _Accum,
_Sat long long _Accum,
_Sat unsigned short _Accum,
_Sat unsigned _Accum,
_Sat unsigned long _Accum,
_Sat unsigned long long _Accum.
I make no apologies for saying this notation is quite ridiculous, it is retrograde, looking more like some 1940s primitive machine code than a 21st century programming language. Of course if the grammar restricts the flexibility for supporting new data types then perhaps the proponents of this can be forgiven.
This is precisely the kind of stuff I was really hoping to hear more about from engineers here, these are precisely the kinds of expansions of language capabilities that I've been talking about, this is why grammar is so so important, the C grammar is a great example of technical debt that has grown and grown and grown.
I think its time the C standard flipped things around, and started with length specific types, and derived bland vague names, like int and short int, from the specific ones - mostly for backwards compatibility and places where the size of the variable is essentially irrelevant, like a small loop counter.QuoteThe fixed-point types are
short _Fract,
_Fract,
long _Fract,
long long _Fract,
unsigned short _Fract,
unsigned _Fract,
unsigned long _Fract,
unsigned long long _Fract,
_Sat short _Fract,
_Sat _Fract,
_Sat long _Fract,
_Sat long long _Fract,
_Sat unsigned short _Fract,
_Sat unsigned _Fract,
_Sat unsigned long _Fract,
_Sat unsigned long long _Fract,
short _Accum,
_Accum,
long _Accum,
long long _Accum,
unsigned short _Accum,
unsigned _Accum,
unsigned long _Accum,
unsigned long long _Accum,
_Sat short _Accum,
_Sat _Accum,
_Sat long _Accum,
_Sat long long _Accum,
_Sat unsigned short _Accum,
_Sat unsigned _Accum,
_Sat unsigned long _Accum,
_Sat unsigned long long _Accum.
I make no apologies for saying this notation is quite ridiculous, it is retrograde, looking more like some 1940s primitive machine code than a 21st century programming language. Of course if the grammar restricts the flexibility for supporting new data types then perhaps the proponents of this can be forgiven.
This is precisely the kind of stuff I was really hoping to hear more about from engineers here, these are precisely the kinds of expansions of language capabilities that I've been talking about, this is why grammar is so so important, the C grammar is a great example of technical debt that has grown and grown and grown.
...float(dec(32)), float(dec(64)), float(dec(128))
...float(bin(16)), float(bin(32)), float(bin(64)), float(bin(128)), float(bin(256))
float(bin(16)) or half
float(bin(32)) or single
float(bin(64)) or double
float(bin(128)) or quad
float(bin(256)) or oct
‘hr’ or ‘HR’ for short _Fract and _Sat short _Fract
‘r’ or ‘R’ for _Fract and _Sat _Fract
‘lr’ or ‘LR’ for long _Fract and _Sat long _Fract
‘llr’ or ‘LLR’ for long long _Fract and _Sat long long _Fract
‘uhr’ or ‘UHR’ for unsigned short _Fract and _Sat unsigned short _Fract
‘ur’ or ‘UR’ for unsigned _Fract and _Sat unsigned _Fract
‘ulr’ or ‘ULR’ for unsigned long _Fract and _Sat unsigned long _Fract
‘ullr’ or ‘ULLR’ for unsigned long long _Fract and _Sat unsigned long long _Fract
‘hk’ or ‘HK’ for short _Accum and _Sat short _Accum
‘k’ or ‘K’ for _Accum and _Sat _Accum
‘lk’ or ‘LK’ for long _Accum and _Sat long _Accum
‘llk’ or ‘LLK’ for long long _Accum and _Sat long long _Accum
‘uhk’ or ‘UHK’ for unsigned short _Accum and _Sat unsigned short _Accum
‘uk’ or ‘UK’ for unsigned _Accum and _Sat unsigned _Accum
‘ulk’ or ‘ULK’ for unsigned long _Accum and _Sat unsigned long _Accum
‘ullk’ or ‘ULLK’ for unsigned long long _Accum and _Sat unsigned long long _Accum
dcl counter fixed bin (P,S);
dcl counter1 fixed bin (18,7); // precision=18, scale=7
dcl counter2 fixed bin (29,3);
dcl counter2 fixed bin (30,-6);
Interesting presentation on the difference between a coroutine and a thread:
https://www.educba.com/coroutines-vs-threads/ (https://www.educba.com/coroutines-vs-threads/)
If the language is going to be used for IoT or Edge computing, it might be helpful to have parallel computing as part of the language. Fortran already has this feature as part of the language specification.
By parallelism are you thinking of multiple cores? or preemptive multitasking on a single core?
One thing that I find problematic with Python is that variables are not declared, they just happen. Given a variable name, you have no idea what is being represented. Could be a string, a float, an int, probably a vector, maybe a matrix and even if you thought you knew the shape, even that could change during execution. How far back do you have to look to find where it was last modified?my old Basic language has this feature, i ended up enforcing "Option Explicit" everytime so i know what i'm coding. in my dictionary, "Option Implicit" and the things you mentioned about returning multiple values is just good recipe for bloatness. btw i think you are not in love with Python, you are in love with its library ;) cheers.
Same thing with MATLAB for that matter.
One thing I like about Modern Fortran is 'implicit none'. Every variable has to be declared and no more of the leading character determining the type unless otherwise declared.
It seems to me that the multiplicity of numeric types will ultimately lead to a trainwreck.
One thing that I find problematic with Python is that variables are not declared, they just happen. Given a variable name, you have no idea what is being represented. Could be a string, a float, an int, probably a vector, maybe a matrix and even if you thought you knew the shape, even that could change during execution. How far back do you have to look to find where it was last modified?
Same thing with MATLAB for that matter.
One thing I like about Modern Fortran is 'implicit none'. Every variable has to be declared and no more of the leading character determining the type unless otherwise declared.
I also like the 'intent' attribute:
http://www.personal.psu.edu/jhm/f90/statements/intent.html (http://www.personal.psu.edu/jhm/f90/statements/intent.html)
I'm not sure what to think about functions returning multiple values and the ability to ignore pieces of the return values. Returning two ndarrays and only keeping one seems bizarre. But so does using indentation as a syntactic element;
I do like the idea of slicing arrays and being able to concatenate rows or columns.
Although I grumble about the lack of declarations, I do like Python's ndarray.
Is white space going to be significant? At least in Fortran IV, it wasn't. The statement:
DO10I=1,4
could be the beginning of a loop or just another real variable being set to a value all the way up to the comma. I never tested that concept but it was a side effect of the fact that Fortran ignored white space. Apparently in fixed format Modern Fortran, white space is still ignored but in free form it is significant
https://community.intel.com/t5/Intel-Fortran-Compiler/spaces-not-ignored-in-free-format/td-p/1112317 (https://community.intel.com/t5/Intel-Fortran-Compiler/spaces-not-ignored-in-free-format/td-p/1112317)
Embedded spaces (or under scores) in long numeric strings can be useful.
SpeedOfLight = 186_000 miles per second
Still it was the first language that contained decent string handling capabilities, pointers, three types of allocation of storage -- static, automatic (stack-based) and controlled (heap-based), exception handling, and rudimentary multitasking. While the idea of preprocessor (a front end macro generator) was never cleanly implemented (as PL/1 did not stored "macro source" line numbers and without them matching preprocessor and "real statements" was difficult) it was also innovative and later was inherited and expanded by C.
All-in-all PL/1 was and probably still is one of the most innovative programming languages in existence and some of its feature are still not matched by "new kids" in the programming language block.
It seems to me that the multiplicity of numeric types will ultimately lead to a trainwreck.
One thing that I find problematic with Python is that variables are not declared, they just happen. Given a variable name, you have no idea what is being represented. Could be a string, a float, an int, probably a vector, maybe a matrix and even if you thought you knew the shape, even that could change during execution. How far back do you have to look to find where it was last modified?
Is white space going to be significant? At least in Fortran IV, it wasn't. The statement:
DO10I=1,4
could be the beginning of a loop or just another real variable being set to a value all the way up to the comma. I never tested that concept but it was a side effect of the fact that Fortran ignored white space. Apparently in fixed format Modern Fortran, white space is still ignored but in free form it is significant
btw i think you are not in love with Python, you are in love with its library ;) cheers.
I would have loved Algol but we didn't have it on the machines I was using circa '70. Fortran or RPG was about all we could get. No worries, a couple of assembly language libraries and we had string functionality in Fortran. IBM provided the Business Subroutines package which we improved locally.
Next high level language up was Pascal and I was in love. It just looks pretty on the listing. Most important to me was the concept of nested procedures/functions. When I found out that C didn't have that feature I was ready to throw rocks at it.
I also like Wirth's syntax diagrams and the way he structure the language such that a recursive descent compiler was all that was needed. I'm big on recursive descent and nested procedures/functions. "Algorithms + Data Structures = Programs" by Niklaus Wirth is my favorite book on programming. The PL0 compiler in the back of the book shows just how simple it is to create a recursive descent compiler. Alas, it is nearly impossible to pass arbitrary arrays with Pascal and reshaping an array would be right out the window. I don't know where it stands with Oberon.
I pretty much like all of the languages that are derived from Algol. I used PL/I in grad school to write an 8080 assembler (this was in '75, things hadn't evolved very far and free assemblers weren't all over the place). I regret not having another semester to really learn the language. My advisor thought the code looked a lot like Fortran. Deadlines...
After it was no longer relevant, I got a copy of PL/M - it is written in Fortran!
Digital Research (CP/M) created a PL/I compiler for the z80/8080 world. It works well but I haven't used it for anything serious. I did use Microsoft's Fortran compiler. I also works well.
Next high level language up was Pascal and I was in love. It just looks pretty on the listing. Most important to me was the concept of nested procedures/functions. When I found out that C didn't have that feature I was ready to throw rocks at it.
Next high level language up was Pascal and I was in love. It just looks pretty on the listing. Most important to me was the concept of nested procedures/functions. When I found out that C didn't have that feature I was ready to throw rocks at it.
I thought so at the time too, but in retrospect it's a half-arsed feature that is bad for the same reason global variables are bad.
Sure, it's slightly handy for creating comparison functions to pass to sort(), but there are few other good use-cases, and it puts the overhead of maintaining a static link chain or "display" into every single function call and return, for a feature that is seldom used.
In Pascal any variables used by the nested function have to be declared right up at the top of the function, with the nested function up there too, and then the actual use is buried who knows how far down in the body of the function.
In C, you put such comparison functions just before the outer function header, instead of just after, and maybe define a small struct to hold variables (copies or pointers to them) shared between the caller and the "nested" function. The caller needs to allocate the struct, copy a couple of things into it, then pass it as an explicit void* parameter to the sort function. The comparison function needs to cast the void* to the correct type.
C seems like more work, but in fact it is work that Pascal has to do anyway, it's just hidden. In fact, every Pascal I've used did it another way -- the previously mentioned static chains or display -- but they SHOULD have done it the same way you manually do it in C, so that the overhead exists only when you actually use the feature, not in every single function call.
The Pascal feature is just pure sugar, not anything fundamental.
What IS useful and much better than C is when you can write the comparison function right there as an anonymous function in the sort function argument, and it can access variables local to the block it is in, not only top level variables of the enclosing function. Even if, as in PL/1, you have to give the nested function a name and write it as a separate statement on the line before the call to the sort() function, that's still fine.
The other annoying part about how you do it in C is having to cast from a void* inside the "nested" function. This is avoided in C++, Java etc by making sort() take a pointer to an object of some class ("comparable"?) and making your compare function a member of a subclass derived from it.
I seem to recall Oberon didn't have classes but had "extensible types", including records.
Full closures, that you can return from the enclosing function, or store into global data structures, are off course much more useful than mere nested functions. That is something you can do in plain C simply by heap-allocating that little struct you copy the shared local variables into.
In addition to svick's great answer there is one more advantage to local functions:
They can be defined anywhere in the function, even after the return statement.
In C, you put such comparison functions just before the outer function header, instead of just after, and maybe define a small struct to hold variables (copies or pointers to them) shared between the caller and the "nested" function. The caller needs to allocate the struct, copy a couple of things into it, then pass it as an explicit void* parameter to the sort function. The comparison function needs to cast the void* to the correct type.A good example of this is the GNU extension to the qsort() function, qsort_r() (https://man7.org/linux/man-pages/man3/qsort_r.3.html). The qsort_r() takes an additional void pointer, that is passed to the comparison function as the third parameter. In particular, if your array entries contain an array of potential keys, you can provide the key index via that extra argument (via (void *)(uintptr_t)index and (uintptr_t)pointer casts). Alternatively, you can use a single comparison function per key type, and pass the byte offset to the key within each structure via that extra argument (using offsetof()).
constbegsys,simptypebegsys,typebegsys,blockbegsys,selectsys,facbegsys,
statbegsys,typedels: setofsys;
Now those that like Pascal - already mentioned that, but feel free to try freepascal and Lazarus. Great stuff if you like that. Definitely underrated.
If you look at the Pascal syntax diagram you will start to see where recursive descent was a part of the syntax and that nested procedures are the way to go:
The underlying problem it's trying to solve is a problem of scope and namespaces. Which can/could be solved using other, more readable approaches.
sizeof, typeof, compatible_types, and typenameof operators
If you look at the Pascal syntax diagram you will start to see where recursive descent was a part of the syntax and that nested procedures are the way to go:
I don't see the link.
Mutually-recursive procedures, yes. But not nested.
If you look at the Pascal syntax diagram you will start to see where recursive descent was a part of the syntax and that nested procedures are the way to go:
I don't see the link.
Mutually-recursive procedures, yes. But not nested.
procedure expression;
var lattr: attr; lop: operator; typind: char; lsize: addrrange;
procedure simpleexpression(fsys: setofsys);
var lattr: attr; lop: operator; signed: boolean;
procedure term(fsys: setofsys);
var lattr: attr; lop: operator;
procedure factor(fsys: setofsys);
var lcp: ctp; lvp: csp; varpart: boolean;
cstpart: setty; lsp: stp;
begin
if not (sy in facbegsys) then
begin error(58); skip(fsys + facbegsys);
gattr.typtr := nil
In C, you put such comparison functions just before the outer function header, instead of just after, and maybe define a small struct to hold variables (copies or pointers to them) shared between the caller and the "nested" function. The caller needs to allocate the struct, copy a couple of things into it, then pass it as an explicit void* parameter to the sort function. The comparison function needs to cast the void* to the correct type.A good example of this is the GNU extension to the qsort() function, qsort_r() (https://man7.org/linux/man-pages/man3/qsort_r.3.html). The qsort_r() takes an additional void pointer, that is passed to the comparison function as the third parameter. In particular, if your array entries contain an array of potential keys, you can provide the key index via that extra argument (via (void *)(uintptr_t)index and (uintptr_t)pointer casts). Alternatively, you can use a single comparison function per key type, and pass the byte offset to the key within each structure via that extra argument (using offsetof()).
I for one use closures –– structures describing the state of a function or operation ––, a lot: in C for example, where one might use nested functions or coroutines in other languages. A typical example is a tokenizing input buffer, a generator of sorts, where one call returns the next field in the current record, and another call skips to the start of the next record, used for example for CSV file parsing. Queues and FIFOs are another use case, when one side may be generating or consuming more data than may fit in RAM, so the two do need to be interleaved to keep the temporary memory requirements within reason.
A nested function is simply one that receives a closure describing the state of its parent – including any variables it needs access to. This works even when the 'nested' function is recursive. Furthermore, such a 'nested' function can be 'nested' under any parent function that uses the same type of closure to describe its state. So no, I don't really need nested functions either, as long as using such closures/state structures is easy and straightforward.
sizeof, typeof, compatible_types, and typenameof operators should be provided even by statically types languages.
C provides only the first one; GCC, Clang, Intel CC and others provide the two next ones as extensions to C; and the fourth one can only be approximated by preprocessor stringification. This leads to less than optimal interfaces when values of runtime-dependent types need to be passed. Typically, the calls need to explicitly specify the type as a literal (token or string in C) and the size of the value, but there is no good way to check they match the expression being passed. (If the expression type is a structure with a C99 flexible array member, even sizeof won't work.)
In particular, stringifying any typeof (expression) will only lead to a string containing that text, not the type name.
These kinds of use cases are why I believe so strongly that looking at existing code (in different competing languages, suitable for the target niche) to understand what kind of approaches they use for solving these kinds of problems, is extremely important. Because there is a lot of bad code out there, looking for consensus is not a good idea: you need to find the 'best' ways of solving each kind of a problem, and learn from that. (To me, 'best' here means robust with errors easy to catch, without imposing a strict usage pattern; and not 'me like most'.)
If you look at the Pascal syntax diagram you will start to see where recursive descent was a part of the syntax and that nested procedures are the way to go:
I don't see the link.
Mutually-recursive procedures, yes. But not nested.
Try this:Code: [Select]procedure expression;
var lattr: attr; lop: operator; typind: char; lsize: addrrange;
procedure simpleexpression(fsys: setofsys);
var lattr: attr; lop: operator; signed: boolean;
procedure term(fsys: setofsys);
var lattr: attr; lop: operator;
procedure factor(fsys: setofsys);
var lcp: ctp; lvp: csp; varpart: boolean;
cstpart: setty; lsp: stp;
begin
if not (sy in facbegsys) then
begin error(58); skip(fsys + facbegsys);
gattr.typtr := nil
Page 138 and 139 of the syntax diagrams show the relationship between expression, simpleexpression, term and factor. Note that factor can recursively call expression and start the entire tree over again.
The compiler code flows directly from the syntax diagrams.
Compared to the transition matrix we were taught in grad school, Wirth's approach looks elegant.
proc main
{
// left_offset = 0;
// left_frame = XXXX
// right_offset = 0;
// right_frame = YYYY
// curr_frame = left_frame;
// goto left_offset
call left (X); // must create a collective stack frame that all coroutines will (in essence) share.
}
proc left(L) coroutine
{
arg L;
// dcl locals
// do something
yield to right(Z); // actually a goto right_offset into right and set left_offset to 1 and curr_frame to right_frame
// do something else
yield to right(Z); // actually a goto right_offset into right and set left_offset to 2 and curr_frame to right_frame
}
proc right(R) coroutine
{
arg R;
// dcl locals
// do something
yield to left(W); // actually a goto left_offset into left and set right_offset to 1 and curr_frame to left_frame
// do something else
yield to left(W); // actually a goto left_offset into left and set right_offset to 2 and curr_frame to left_frame
}
yield (item);
yield to some_procedure;
yield to some_procedure(args);
yield (item) to some_procedure;
yield (item) to some_procedure(args);
x = yield to some_procedure;
x = yield to some_procedure(args);
x = yield (y) to some_procedure(args);
a = yield (x) to somewhere(y);
I have coded up that PL/0 compiler several times, once in Fortran. Dozens of pages of Fortran or a very few pages of Pascal. I'll concede that the Fortran was crap code but, really, sets aren't part of the language and they are terribly important to the compiler.
Wirth defined a simple language named PL/0 too that he used to convey this idea.
I'll tell you another truly superb book on compiler design, one I used a lot in the past it is Understanding and Writing Compilers (https://www.amazon.com/Understanding-Writing-Compilers-do-yourself/dp/0333217322) by Richard Bornat. It is oriented to recursive descent and is very readable, the author has a deep understanding of machine architectures and code generation practicalities, gives easy to follow examples akin to the stuff Wirth wrote, I doubt I could have gotten as far as I did with my PL/I implementation without this book.I ordered a copy from alibris.com - $15 including shipping. I get a lot of used books from Alibris. I suspect that's the way my library will go as well.
The only thing that annoys me is that the linker does not yet know how to sort those entries at link time.
I have coded up that PL/0 compiler several times, once in Fortran. Dozens of pages of Fortran or a very few pages of Pascal. I'll concede that the Fortran was crap code but, really, sets aren't part of the language and they are terribly important to the compiler.
Wirth defined a simple language named PL/0 too that he used to convey this idea.
Fortunately, the CDC 6400 on which the compiler was developed had a 60 bit word and that was enough bits to accommodate the elements. It gets ugly when you have to compare across 8 or 16 bit or even 32 bit entities. Then there is the lack of formal recursion in Fortran (I believe I was using a IV version of Fortran).QuoteI'll tell you another truly superb book on compiler design, one I used a lot in the past it is Understanding and Writing Compilers (https://www.amazon.com/Understanding-Writing-Compilers-do-yourself/dp/0333217322) by Richard Bornat. It is oriented to recursive descent and is very readable, the author has a deep understanding of machine architectures and code generation practicalities, gives easy to follow examples akin to the stuff Wirth wrote, I doubt I could have gotten as far as I did with my PL/I implementation without this book.I ordered a copy from alibris.com - $15 including shipping. I get a lot of used books from Alibris. I suspect that's the way my library will go as well.
If you look at the Pascal syntax diagram you will start to see where recursive descent was a part of the syntax and that nested procedures are the way to go:
I don't see the link.
Mutually-recursive procedures, yes. But not nested.
Try this:Code: [Select]procedure expression;
var lattr: attr; lop: operator; typind: char; lsize: addrrange;
procedure simpleexpression(fsys: setofsys);
var lattr: attr; lop: operator; signed: boolean;
procedure term(fsys: setofsys);
var lattr: attr; lop: operator;
procedure factor(fsys: setofsys);
var lcp: ctp; lvp: csp; varpart: boolean;
cstpart: setty; lsp: stp;
begin
if not (sy in facbegsys) then
begin error(58); skip(fsys + facbegsys);
gattr.typtr := nil
Page 138 and 139 of the syntax diagrams show the relationship between expression, simpleexpression, term and factor. Note that factor can recursively call expression and start the entire tree over again.
The compiler code flows directly from the syntax diagrams.
Compared to the transition matrix we were taught in grad school, Wirth's approach looks elegant.
sy
facbegsys
error
skip
fsys
gattr
I have coded up that PL/0 compiler several times
Fortunately, the CDC 6400 on which the compiler was developed had a 60 bit word and that was enough bits to accommodate the elements. It gets ugly when you have to compare across 8 or 16 bit or even 32 bit entities.
learning on how to make an AI/physics/math/fancy data structure/class library in C or world/3d/game engine is much more fun and gives much more practical use... imho. cheers.QuoteI'll tell you another truly superb book on compiler design, one I used a lot in the past it is Understanding and Writing Compilers (https://www.amazon.com/Understanding-Writing-Compilers-do-yourself/dp/0333217322) by Richard Bornat. It is oriented to recursive descent and is very readable, the author has a deep understanding of machine architectures and code generation practicalities, gives easy to follow examples akin to the stuff Wirth wrote, I doubt I could have gotten as far as I did with my PL/I implementation without this book.I ordered a copy from alibris.com - $15 including shipping. I get a lot of used books from Alibris. I suspect that's the way my library will go as well.
I didn't want to paste a thousand lines of code so I only copied the first few lines just to show the hierarchy. The remaining code is easy to find at the link.
Certainly the code could have been written without nesting but it is cleaner with it. Only procedure 'term' can call procedure 'factor' so the nesting cleans up the namespace by default. Going uphill, 'factor' can call 'expression' just not the other way around. It's hard to provide this functionality with all procedures at the same level.
learning on how to make an AI/physics/math/fancy data structure/class library in C or world/3d/game engine is much more fun and gives much more practical use... imho. cheers.
learning on how to make an AI/physics/math/fancy data structure/class library in C or world/3d/game engine is much more fun and gives much more practical use... imho. cheers.
I have no artistic capability whatsoever. Creating assets, particularly characters is out of the question.
I don't play video games - ever; I maxed out at PacMan. I did look at Blender and Unreal just to get some idea of the workflow but I came to the immediate realization that it takes more than one person to create a game. A cast of hundreds wouldn't surprise me.
I have no artistic capability whatsoever.creating compiler that works with your machine, or better than the others, is an art in itself...
I have no artistic capability whatsoever.creating compiler that works with your machine, or better than the others, is an art in itself...
rate = 128,000;
freq = 100,000,000;
ramp = 12,456.558;
When defining a new language, at the stage where irreversible decisions are yet to be made, there are a number of things we can look at in a fresh light.Commas inserted like that are very confusing in countries which don't use that notation, and especially in countries that use a comma as the decimal point. Software exists in a very international market. Try to keep it as generic as possible. Should we support numbers like 1,00,00,000? Indians would be very happy with that notation. I probably wouldn't.
One of these is the lexical structure of numeric literals, a tiny change to the token definitions for these means we can easily support optional commas:Code: [Select]
rate = 128,000;
freq = 100,000,000;
ramp = 12,456.558;
None of these present any problems at all insofar as lexical analysis ("recognizing") is concerned and have a definite pragmatic appeal. Of course there are cultural differences, but since we're not dealing with data but rather programming language notation, the comma is no less reasonable than the period.
a = 1_00_00 # you do not need to group digits by 3!
b = 0xbad_c0ffee # you can make fun with hex digit notation
c = 0b0101_01010101010_0100 # works with binary notation
f = 1_000_00.0
print(a,b,c,f)
Where are [compatible_types and typenameof] useful in C?typenameof (expression) would correspond to C STRINGIFY(typeof(expression)) if it was supported and actually worked. Essentially, it allows a library to implement run-time interfaces like
I was referring there to sorting the contents of a section, treating it as an array of N structures of size L with a sort key of type T at offset K.The only thing that annoys me is that the linker does not yet know how to sort those entries at link time.There is SORT_BY_NAME (or simply SORT) keyword for input section description, does it not work?
When defining a new language, at the stage where irreversible decisions are yet to be made, there are a number of things we can look at in a fresh light.Commas inserted like that are very confusing in countries which don't use that notation, and especially in countries that use a comma as the decimal point. Software exists in a very international market. Try to keep it as generic as possible. Should we support numbers like 1,00,00,000? Indians would be very happy with that notation. I probably wouldn't.
One of these is the lexical structure of numeric literals, a tiny change to the token definitions for these means we can easily support optional commas:Code: [Select]
rate = 128,000;
freq = 100,000,000;
ramp = 12,456.558;
None of these present any problems at all insofar as lexical analysis ("recognizing") is concerned and have a definite pragmatic appeal. Of course there are cultural differences, but since we're not dealing with data but rather programming language notation, the comma is no less reasonable than the period.
Blatantly stolen, from here https://stackoverflow.com/questions/54009778/what-do-underscores-in-a-number-meanCode: [Select]a = 1_00_00 # you do not need to group digits by 3!
b = 0xbad_c0ffee # you can make fun with hex digit notation
c = 0b0101_01010101010_0100 # works with binary notation
f = 1_000_00.0
print(a,b,c,f)
It's not pretty, and I prefer to specify big number in other fashions myself. But if you have an integer/constant/bitmask with tons of significant digits I suppose it may be helpful.
a = 10:H; // 16 in hex
b = 1245 77AC:H; // also hex with optional separator
c = 1001 0110:B; // binary with space
d = 123,456.77; // conventional decimal with separator
e = 123,456.77:D // explicit base - optional.
call initialize(100,345.88,severity);
If you do consider the cultural differences then clearly we should not restrict the decimal point to being a PERIOD yet we do, universally in all programming languages.That's not quite true. Have you ever seen French or German Fortran? There was a lot of localisation of computer languages in the early days. The keywords were localised. Things like decimal points and other formatting issues were localised. However, it made such a pain of passing stuff around in multi-national organisations that by the end of the 60s computer languages has pretty much abandoned this approach.
If you do consider the cultural differences then clearly we should not restrict the decimal point to being a PERIOD yet we do, universally in all programming languages.That's not quite true. Have you ever seen French or German Fortran? There was a lot of localisation of computer languages in the early days. The keywords were localised. Things like decimal points and other formatting issues were localised. However, it made such a pain of passing stuff around in multi-national organisations that by the end of the 60s computer languages has pretty much abandoned this approach.
FA068AB7:H // hex literal
FA06 8AB7:H // hex literal
FA06_8AB7:H // hex literal
FA06 __ 83BC 8AB7:H // hex literal (I'm going to reject such a monster though either have space OR underscore and no more than one at a time)
FA068AB7 // ordinary identifier. An identifier composed wholly of hex characters should perhaps be disallowed, inadvisable...
FA06 8AB7 : H // hex literal
It would be very easy indeed to support alternatively spelled keywords (especially here where the grammar has no reserved words) but I'd consider that not a "phase 1" thing for me!There was at least one Fortran compiler where DAMNITALL could be used for DIMENSION - I guess the counted 9 chars starting with D and called it good. They could almost get away with that as long as all declarations had to precede any executable statements. Maybe they looked for the 3rd char to be M, I don't know...
So far as the numeric notation goes, I've settled for this, these are all examples of stuff that the lexer is recognizing just now:Code: [Select]
FA068AB7 // ordinary identifier. An identifier composed wholly of hex characters should perhaps be disallowed, inadvisable...
It would be very easy indeed to support alternatively spelled keywords (especially here where the grammar has no reserved words) but I'd consider that not a "phase 1" thing for me!There was at least one Fortran compiler where DAMNITALL could be used for DIMENSION - I guess the counted 9 chars starting with D and called it good. They could almost get away with that as long as all declarations had to precede any executable statements. Maybe they looked for the 3rd char to be M, I don't know...QuoteSo far as the numeric notation goes, I've settled for this, these are all examples of stuff that the lexer is recognizing just now:Code: [Select]
FA068AB7 // ordinary identifier. An identifier composed wholly of hex characters should perhaps be disallowed, inadvisable...
Including FACE? Seems a reasonable variable name for image recognition.
I can't say I'm a fan of the proposal. I find the C notation more than adequate for common bases and I'm not too sure there is any utility for other bases. Even base 8 is going the way of Digital Equipment Corporation - remember them? Realistically, for coding we have bases 2, 8 and 16. We run out of English language letters before we can do base 32 (fortunately).
I'm also not a fan of the parser having to look so far ahead to determine whether there is a numeric constant or just another variable. Having numerics start with a decimal digit seems reasonable.
One thing about Pascal syntax, the compiler knew exactly what set of symbols could come next at every step of parsing. No backtracking that I know of.
I do like being able to use underscores with long numeric strings but that's as far as I would push it. OTOH, I'm not creating the language...
Just to make sure some wheels do not get reinvented too much, C23 provides:
* typeof (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2912.pdf#subsubsection.6.7.2.5)
* bit-precise integer types (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2912.pdf#subsection.6.2.5)
* binary literals, and a digit separator (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2912.pdf#subsubsection.6.4.4.1) (')
* (optional)decimal floating point types (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2912.pdf#subsection.6.2.5)
Something a new language needs is a precisely defined means of initializing variables and structure instances. I've seen { 0 } can do this but only at the point of declaration it seems.
Something a new language needs is a precisely defined means of initializing variables and structure instances. I've seen { 0 } can do this but only at the point of declaration it seems.
Again, you assume the worst when you simply don't know.
But I can assure you, you can set a struct to {0} any time you want; this is not limited to the initialization at point of declaration.
What may confuse the young players is, because C evaluates right side of expression "on their own" (which simplifies rules and removes guesswork), the compiler can't know the type from {0} alone. In an initializer, it is known (from the variable you are initializing), but in assignment, you need to tell the type:
typedef struct
{
int asdf;
int qwerty;
} our_type_t;
our_type_t object = {0};
object = (our_type_t){0};
easily infer that cast itselfThat's not a cast.
easily infer that cast itselfThat's not a cast.
Very well but it is regularly referred to by that term, and it could be inferred, the compiler has access to enough information to make the inference.
Very well but it is regularly referred to by that term, and it could be inferred, the compiler has access to enough information to make the inference.
Compiler could have that information, yes - but this is one of the fundamental rules of C: in assignments, right side is evaluated on its own, with no information fed-forward from the left side.
This again makes the language rules simpler and more predictable.
The same confusion arises when doing arithmetics with different types:
uint32_t a;
uint32_t b;
uint64_t c;
c = a + b;
Per language rules, the result of (a+b) will be of type uint32_t, and only during the assignment is converted to uint64_t. So if a and b are near the limit or numerical range, the result will wrap around; maybe the programmer's intent was:
c = (uint64_t)a + (uint64_t)b;
Maybe a more "clever" language, where the type of destination of assignment was considered, would be good? It would remove one footgun. On the other hand, it would create more subtle ones, as the rules of conversions would get even more complicated than they already are. With current solution, you at least quickly learn the rule - right side is in isolation - and can control the necessary conversions yourself.
In fact, we can go further - if you want such automation, why stop there? Why not remove all the manual type hassle completely? Python does that, and infers the type automagically for you. Some like it, some do not; but clearly the use cases are different. Those who work with microcontrollers, often need the control.
int ZZZ = 123456;
char MMM;
MMM = ZZZ;
int ZZZ = 123456;
char MMM;
MMM = (char)ZZZ;
MyType datum;
datum = (MyType){0};
SomeType A;
A = (SomeOtherType){0};
Code: [Select]
int ZZZ = 123456;
char MMM;
MMM = ZZZ;
The compiler does not object and insist I do this:
Look at every example in real code, you'll never find an example of:Code: [Select]
SomeType A;
A = (SomeOtherType){0};
typedef struct { int a; } type1_t;
typedef struct { char b; } type2_t;
int main()
{
type1_t t1;
t1 = (type1_t){0}; // OK
t1 = (type2_t){0}; // error
return 0;
}
hrst@jorma:~$ gcc t2.c
t2.c: In function ‘main’:
t2.c:10:7: error: incompatible types when assigning to type ‘type1_t’ {aka ‘struct <anonymous>’} from type ‘type2_t’ {aka ‘struct <anonymous>’}
10 | t1 = (type2_t){0};
twotypes.h
typedef struct {uint32_t thing} thing_wide_t; // high numerical range thing
typedef struct {uint16_t thing} thing_narrow_t; // low-cost version of thing, same accuracy, less range
static inline thing_narrow_t wide_to_narrow(thing_wide_t in)
{
thing_narrow_t ret;
ret.thing = (in.thing > UINT16_MAX):UINT16_MAX:in.thing; // saturate instead of overflow
return ret;
}
Code: [Select]
int ZZZ = 123456;
char MMM;
MMM = ZZZ;
The compiler does not object and insist I do this:
Because compiler knows ZZZ is of type int! Hence, ZZZ can be used as right-side operator in assigments, it can be used in comparisons, arithmetics - wherever.
But you can't write
datum = {0};
only because the language has no idea how to interpret {0}. At lowest level, how many bytes does {0} consist of? No idea, without the type. Compared to that, 0 is treated as int internally. But there is no such default interpretation for {0} (what would it be?), so you need to specify the type (with the cast-like syntax).QuoteLook at every example in real code, you'll never find an example of:Code: [Select]
SomeType A;
A = (SomeOtherType){0};
You won't find such example, because it won't compile, because that assignment is invalid as there is no implicit conversion available for the assignment. Yes, C is more strongly typed than you might think!Code: [Select]typedef struct { int a; } type1_t;
typedef struct { char b; } type2_t;
int main()
{
type1_t t1;
t1 = (type1_t){0}; // OK
t1 = (type2_t){0}; // error
return 0;
}
hrst@jorma:~$ gcc t2.c
t2.c: In function ‘main’:
t2.c:10:7: error: incompatible types when assigning to type ‘type1_t’ {aka ‘struct <anonymous>’} from type ‘type2_t’ {aka ‘struct <anonymous>’}
10 | t1 = (type2_t){0};
This is why you need to decide how to convert between those types, and create a conversion function, like:Code: [Select]twotypes.h
typedef struct {uint32_t thing} thing_wide_t; // high numerical range thing
typedef struct {uint16_t thing} thing_narrow_t; // low-cost version of thing, same accuracy, less range
static inline thing_narrow_t wide_to_narrow(thing_wide_t in)
{
thing_narrow_t ret;
ret.thing = (in.thing > UINT16_MAX):UINT16_MAX:in.thing; // saturate instead of overflow
return ret;
}
Built-in integer types, for example, have built-in conversions.
DataStructure datum = {0};
MyStructure a = {0};
MyStructure b;
b = {0};
Code: [Select]
MyStructure a = {0};
MyStructure b;
b = {0};
As a compiler writer, I'm having a hard time understanding the difficulty, surely there's more to all this?
Code: [Select]
MyStructure a = {0};
MyStructure b;
b = {0};
As a compiler writer, I'm having a hard time understanding the difficulty, surely there's more to all this?
In C, a literal constructed with curly brackets has no definite type, and its type can't be derived from the literal itself. What is '{0}'? Are you able to give it a type? Nope.
What is '{1,2,3}'? Not any better. Could be an array (of what), could be a struct (with what members?)
For historical reasons, C accepts such *initializers* for practical purposes. In this context, this in an initializer, not a literal that has semantic by itself.
In C99, they introduced compound literals. But you have to give them a type, otherwise they have none. For backwards compatibility, C99 still accepts initializers which are not fully-formed (so, given a type) compound literals. But it doesn't in the context of an assignment, because in this context, the literal has no type and is thus not an expression you can use (again apart from the context of initialization.)
This is all a semantic question. Sure a compiler could 'guess' the type of a compound literal without explicitely giving it a type in the context of an assignment, but it wouldn't fit in the C language with its promotion and implicit conversion rules.
Fact is, defining a syntax with which 'complex' literals (so beyond base scalar types) fully embed their own type is hard. Depending on the "strength" of your type system, it may or may not matter. For a weakly-type language, or even a dynamically-typed language, it may not matter at all. Also depends on what kind of grammar you define and how context-dependent it is.
So can the above proposal you made be implemented? Of course. Are there "corner cases" for which it could be less trivial? Probably.
If you find that useful, why not. But I have enough gripes about all the implicit conversions in C which allow pretty sloppy programming, so I'm not sure I'd want to add a layer of that with "typeless" compound literals.
MyStructure a = {0};
MyStructure a;
a = {0};
dcl a MyStructure = default();
// code...
a = default();
// for any type.
MyStructure a();
...
a = MyStructure();
some_pointer = nullptr;
I don't mind typing a bit of extra information, when it conveys important information for us programmers, like the type. This adds safety; the C standard requiring me to type the type (pun intended), means:
* I need to keep my brain enabled, less likely to do a mistake with the type
* If I make a mistake and use a wrong type, the compiler will check it for me and error out
* If I want to assign or compare incompatible types, that is only possible by writing the required conversion functions (or doing nasty pointer casting etc., which pokes in the eye)
This is a feature of a strongly typed language. And yet, I would prefer C to be even more strongly typed. Implicit conversions are classic footguns. I don't want any more of them, and oh boy how much I struggled with PHP and its dynamic typing system (although PHP is a disaster in every other regard, too).
Trying to make typing a bit faster is not a valid target. Less than 1% of time in programming is spent actually writing characters on code editor screen.
I like to point out that in C the variables are strongly typed, whereas the values (i.e. what's out there in the memory) is untyped. That's how you can cast a car to be a camel.
// ah crud someone banned the following
// camel = *(camel_t*)&car;
extern camel *car_to_camel(car_t *car);
camel = *(car_to_camel(&car));
car_to_camel:
camel_to_car:
camel_to_horse:
horse_to_camel:
horse_to_car:
car_to_horse:
ret
In c++ you can already do that, with slightly different syntax:Code: [Select]MyStructure a();
...
a = MyStructure();
And there is also nullptr:Code: [Select]some_pointer = nullptr;
Initialization is indeed exception to the rule. Such is life, rules are arbitrary and not always perfectly logical. That does not mean rules are stupid or useless. I don't mind typing a bit of extra information, when it conveys important information for us programmers, like the type. This adds safety; the C standard requiring me to type the type (pun intended), means:
* I need to keep my brain enabled, less likely to do a mistake with the type
* If I make a mistake and use a wrong type, the compiler will check it for me and error out
* If I want to assign or compare incompatible types, that is only possible by writing the required conversion functions (or doing nasty pointer casting etc., which pokes in the eye)
This is a feature of a strongly typed language. And yet, I would prefer C to be even more strongly typed. Implicit conversions are classic footguns. I don't want any more of them, and oh boy how much I struggled with PHP and its dynamic typing system (although PHP is a disaster in every other regard, too).
Trying to make typing a bit faster is not a valid target. Less than 1% of time in programming is spent actually writing characters on code editor screen.
int a;
short b;
long c;
char d;
a = (int)100;
b = (short)100;
c = (long)100;
d = (long)100;
I like to point out that in C the variables are strongly typed, whereas the values (i.e. what's out there in the memory) is untyped. That's how you can cast a car to be a camel.
This weird mix of safety and unsafety is also what makes C powerful and popular: it offers decent level of safety (camel = car is not possible), but also easy enough mechanisms to bypass that safety: camel = *((*camel_t)&car) (or the same via union type punning since C99 or so*). But the latter still - usually! - happens on purpose. Much better than by accident.
There are and always will be three classes of people:
* Those who think C is too unsafe and too weakly typed, and should not allow bypassing type safety with any syntax,
* Those who think C is too tedious to work with with all that type bullshit ("come on, I don't want to type, why can't compiler do it for me?")
* Those who are relatively satisfied with the compromise.
It's worth understanding the first two are actually polar opposite of each other. Sometimes you can see a "C complainer" complain about these two opposite problems in the same post, which makes it look... schizophrenic. Or just that sometimes people just want to complain, without having any idea what they are talking about. (And I'm not referring to anyone particular this time.)
// fixed point decimal literals
1
11
101010
010101
100 000
100_000
256:D
123.45
// fixed point binary literals
1:B
101.101:B
001 110 010:B
101 110.110:b
110_001:B
11 01101 01.1:B
// fixed point hexadecimal literals
E:H
17FA:H
0AC0 7F4A:H
1AC0_7F4A:h
4AC6.7C3:H
// Octal literals (no justification/use case for octal fractions...?)
274:O
11 223 752:O
11_223_752:O
// floating point hex literals - 'p' (or 'P') eliminates the need for base designator
123.FE6p+3
123 334.FE6P-3
E05_22B.02p2
// floating point binary literals - 'b' (or 'B') eliminates the need for base designator
110.00101b+4
110.00101b3
1.0101101b-2
Well the inability of C to deduce (I use that word intentionally because there is no uncertainty) the type when we assign a value {0} is nothing to do with strong or weak typing. The type of the target is known and fixed, at compile time.
If being explicit about the type is so helpful then why do we not see this on every assignment:
this is going to be a looong thread :popcorn:QuoteIf being explicit about the type is so helpful then why do we not see this on every assignment:The problem with yourself is...
this is going to be a looong thread :popcorn:QuoteIf being explicit about the type is so helpful then why do we not see this on every assignment:The problem with yourself is...
Well the inability of C to deduce (I use that word intentionally because there is no uncertainty) the type when we assign a value {0} is nothing to do with strong or weak typing. The type of the target is known and fixed, at compile time.
Sure is, but target is target! It's different thing! Did you not realize you can assign variables of different types to each other if they are compatible and have conversions? For example, uint32_t can be assigned into uint8_t, because narrowing conversion exists.QuoteIf being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
I already mentioned that 0 is int, this is in language specification, but you ignored this and wasted our time to provide the stupid pseudo example I wanted to avoid by mentioning this early on.
This is why you don't need to specify the type of the literal 100, it's int by default. You can however modify the type, for example 100ULL is unsigned long long int. And you can assign int into float with built-in conversion, so you can write float a = 100. Isn't C fancy?
char number;
number = 100; // no need to prefix with (char)...an implicit conversion will be inserted.
But what would {0} be? Would it be {int}, or maybe {int, int}, or how about {int, int, int}? Tell me. (And no, "same type as target of assignment" is not the answer.)
So C has already rules how to interpret literals, and how to convert between types. But it has no rule how to interpret more complex compound literals based on some other nearby code.
If being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.
So what is it? is the 100 a char or an int? Yes, it IS an int and it is being assigned to a char without a (char) on the basis of your earlier argument about "safety" and "footguns" I must assume that you always insert the (char) in such cases, if not why not?
If being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
Yes, everything does indicate that.
Now there's nothing wrong with not having read everything; we all start out as beginners. Nor is there anything wrong with going through some of the same thought processes as people have done in the past.
But not knowing the literature, not bothering to read things people suggest would advance his understanding and thinking, and still expecting other people to "discuss" and "explain" things is bad form.Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.
I, and some others, decided that a while ago. The only reason I bother to look at this thread is that some of the peripheral things mentioned by other people are interesting.
But, hey, go knock yourself out :)
So what is it? is the 100 a char or an int? Yes, it IS an int and it is being assigned to a char without a (char) on the basis of your earlier argument about "safety" and "footguns" I must assume that you always insert the (char) in such cases, if not why not?
That's actually a good question. Yes, sometimes I write explicit cast, sometimes not. The reason not to is when it is obvious what is going to happen anyway (I assume the reader knows the basics of the C standard, but don't assume they know every detail by heart). Such case is assigning uint8_t to uint32_t: it is obvious that the input range [0..255] maps to output range [0..255] and extra bits get filled with zeroes.
On the other hand, maybe I want to emphasize the fact extra bits are generated. Not to compiler, but human reader. Then why not add the explicit cast.
Or, when I convert a float to integer, let's say:
double asdf = 123.4;
int val = asdf + 0.5; // round up and truncate after decimal point
One can emphasize the conversion by writing:
int val = (int)(asdf + 0.5);
But, you can just write
int val = asdf + 0.5;
because the implicit conversion does the same.
But explicit makes sense, because the same thing could be used in a more complex expression, where adding the cast does make the difference:
double something_else = (double)(int)(asdf+0.5) * qwerty;
Remember, programming is not only about making compiler happy, but to convey the meaning to human readers, too.
char X;
Data Y;
X = (char)0;
Y = (Data){0};
People get surprisingly defensive about it—perhaps it's Stockholm Syndrome?—but C simply isn't a well-designed language. The fact that the other popular alternatives are more complex and worse (C++? really?) doesn't change that. The fact that you know its edge cases well enough to get by (but probably not well enough to avoid a myriad sources of bugs and security vulnerabilities unique to the language) doesn't change it either.
If being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
Yes, everything does indicate that.
Now there's nothing wrong with not having read everything; we all start out as beginners. Nor is there anything wrong with going through some of the same thought processes as people have done in the past.
But not knowing the literature, not bothering to read things people suggest would advance his understanding and thinking, and still expecting other people to "discuss" and "explain" things is bad form.Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.
I, and some others, decided that a while ago. The only reason I bother to look at this thread is that some of the peripheral things mentioned by other people are interesting.
But, hey, go knock yourself out :)
What exactly have I written that you regard as untrue?
...
If being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
Yes, everything does indicate that.
Now there's nothing wrong with not having read everything; we all start out as beginners. Nor is there anything wrong with going through some of the same thought processes as people have done in the past.
But not knowing the literature, not bothering to read things people suggest would advance his understanding and thinking, and still expecting other people to "discuss" and "explain" things is bad form.Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.
I, and some others, decided that a while ago. The only reason I bother to look at this thread is that some of the peripheral things mentioned by other people are interesting.
But, hey, go knock yourself out :)
What exactly have I written that you regard as untrue?
...
Thereby indicating you didn't read what I wrote.
QED.
Code: [Select]char X;
Data Y;
X = (char)0;
Y = (Data){0};
If I am allowed to omit the (char) then logically I should also be allowed to omit the (Data),
Code: [Select]char X;
Data Y;
X = (char)0;
Y = (Data){0};
If I am allowed to omit the (char) then logically I should also be allowed to omit the (Data),
The expression Y={0} were already syntactically wrong, because {0} is not a valid expression according to the language definition.
A philosophy of C is that each expression has its own intrinsic type which does not depend on the context in which the expression is used. Therefore {0} cannot be a valid expression, because no unique type can be deduced from {0} alone. OTOH, the integer literal expression 0 has an intrinsic type (which is int).
EDIT:
Note that (Data){0} is NOT a cast expression, but a compund literal. The (Data) is part of the literal.
OTOH, (char)0 is a cast expression (unary cast operator) which converts the integer literal 0 to type char.
MyStructure datum = {0};
short X = 0;
short Y; Y = 0;
If being explicit about the type is so helpful then why do we not see this on every assignment:
The problem with yourself is, because you don't read, you never learn.
Yes, everything does indicate that.
Now there's nothing wrong with not having read everything; we all start out as beginners. Nor is there anything wrong with going through some of the same thought processes as people have done in the past.
But not knowing the literature, not bothering to read things people suggest would advance his understanding and thinking, and still expecting other people to "discuss" and "explain" things is bad form.Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.
I, and some others, decided that a while ago. The only reason I bother to look at this thread is that some of the peripheral things mentioned by other people are interesting.
But, hey, go knock yourself out :)
What exactly have I written that you regard as untrue?
...
Thereby indicating you didn't read what I wrote.
QED.
Nor will I be reading anything else you write.
Well this also contains an invalid expression then too surely?Code: [Select]MyStructure datum = {0};
proc initialize (count)
{
arg count bin(8,2); // fixed point binary, 6.2 format
dcl register_mask bin(24,8);
dcl temporary dec(15,3); // fixed point decimal, 12.3 format
count = 110011.01B;
temporary = 23 000 000 000.25;
register_mask = 2F03 D7F8.2:H;
// Or equivalently
register mask = 10 1111 0000 0011 1101 0111 1111 1000.00100000:b;
}
i mean... there are few members in this forum that are persistent enough to update their tiny progress/thought in their own "log thread" for extended amount of time, grow into hundreds of pages... and then later post the latest update saying the previous posts was a mistake... in the end i only look at the thread when somebody else made a new reply in case they provide new informations... to put insult to the pain... esp "language" is a really subjective matter imho. cheers.Likely not, I don't usually play these games for too long, it's getting near to the point of getting nothing out of it.this is going to be a looong thread :popcorn:QuoteIf being explicit about the type is so helpful then why do we not see this on every assignment:The problem with yourself is...
Well this also contains an invalid expression then too surely?Code: [Select]MyStructure datum = {0};
In conjunction with the declaration of datum, the ={0} is an initialization, and {0} is still not an expression, but an initializer-list enclosed in braces.
See https://en.cppreference.com/w/c/language/initialization
Well this also contains an invalid expression then too surely?Code: [Select]MyStructure datum = {0};
In conjunction with the declaration of datum, the ={0} is an initialization, and {0} is still not an expression, but an initializer-list enclosed in braces.
See https://en.cppreference.com/w/c/language/initialization
Yes, what you say is likely absolutely the case, that doesn't justify the design though, in the case of initializing a declaration, populating the lvalue is what happens and there's no need I can see for distinguishing between an lvalue used when we declare vs an lvalue used as the source expression of an assignment.
What I can "do to" the memory associated with some variable shouldn't differ between declaring it and assigning to it, IMHO anyway.
typedef struct { int x; } Data;
int f()
{
return {0}.x;
}
typedef struct { int x; } Data;
int f()
{
return (Data){0}.x;
}
OTOH, with a properly typed compound literal (which is a valid expression) it works as expected, and f() returns 0:Code: [Select]typedef struct { int x; } Data;
int f()
{
return (Data){0}.x;
}
OTOH, with a properly typed compound literal (which is a valid expression) it works as expected, and f() returns 0:Code: [Select]typedef struct { int x; } Data;
int f()
{
return (Data){0}.x;
}
Exactly. You can come up with more examples like this - the point is, the grammar and syntax is logical and repeatable.
There already is one "exception" to this logic, namely initializer list being a different animal from compound literal, and not needing a type.
One could argue C should add more of same type of exceptions, like the "in case of assignment to a struct type, compound literal is assumed to have the type of assignment target, unless otherwise explicitly typed". OK, then what? Now we have introduced more rules to the language, and what did we achieve? We saved a few keystrokes, hid the type from view, and you STILL need to write the type in all other forms of expressions.
Such thinking is very superficial. Again, the number of keystrokes needed is unimportant. Having to type something explicitly is a "problem" only for total beginners, who think majority of time spent in programming is hitting the keys. And any of this has absolutely no fundamental effect on the language design. More fundamental is the fact that these expressions have a type which is known at compile time, to both the programmer and the compiler.
proc initialize (count)
arg count bin(8,2) // fixed point binary, 6.2 format
dcl register_mask bin(24,8)
dcl temporary dec(15,3) // fixed point decimal, 12.3 format
count = 110011.01B
temporary = 23 000 000 000.25
register_mask = 2F03 D7F8.2:H
// Or equivalently
register mask = 10 1111 0000 0011 1101 0111 1111 1000.00100000:b
end
proc initialize (count)
if (a = 23)
a = 0
end
if (a = 55)
a = 10
end
if (a > 1024)
a = 0
call reset(100)
end
if (a < 2048)
a = 50
call restart(0)
else
a = 1024
end
while (state = READY)
call refresh_display(250)
state = get_state()
end
end
Such thinking is very superficial. Again, the number of keystrokes needed is unimportant. Having to type something explicitly is a "problem" only for total beginners, who think majority of time spent in programming is hitting the keys.
if(a == b)
c = 10
end
if(a == b)
{
c = 10;
}
Code: [Select]if(a == b)
c = 10
end
Is basically just as much typing asCode: [Select]if(a == b)
{
c = 10;
}
I myself like the braces. Wish they had used them in verilog in the same way instead of the begin and end... statements. But that comes down to what one is used to.
line:
while (myLine = readLine())
:
if (...) next line;
:
if (...) last line;
:
end while line
I like "end" better, if only because it makes for tidier syntax for signalling what is is you are ending, in a way that the compiler can check (i.e. not just a comment).
........
On the "end" statement, either the "while" or the label can be omitted: "end", "end while", "end line".
If (a = b) Then
c = 10
End If
the problem with it is it doenst support pointer and true OOP.. if Basic can do whatever C can, i think i've left C behind too long time ago...Code: [Select]If (a = b) Then
c = 10
End If
Code: [Select]if(a == b)
c = 10
end
Is basically just as much typing asCode: [Select]if(a == b)
{
c = 10;
}
I myself like the braces. Wish they had used them in verilog in the same way instead of the begin and end... statements. But that comes down to what one is used to.
And frankly this is all cosmetics, nothing to do with improving functionality of a programming language. So I agree with SiliconWizard that it is futile.
Code: [Select]If (a = b) Then
c = 10
End If
That hurts my (somewhat pedantic and logical) eye because the same operator (=) is used for two different purposes. I don't mind using = as comparison operator, but then something else is needed for assignment, such as :=, or, borrowing from mathematics, "let c = 10".
If (a = b)
c = 10
end
If (a = b)
c <= 10
end
we are aware of this, but the compiler is clever enough to know when to assign and when to compare. it also happen to us human, there is difference when we say "a equals b" and "if a equals b", thats why i said, language is too subjective. if you want to impose technically and grammatically correct syntax in communication, good luck!.That hurts my (somewhat pedantic and logical) eye because the same operator (=) is used for two different purposes. I don't mind using = as comparison operator, but then something else is needed for assignment, such as :=, or, borrowing from mathematics, "let c = 10".Code: [Select]If (a = b) Then
c = 10
End If
I understand you, but allowing and assignment to also be an expression is illegal, meaningless in many programming languages.try to make your language same popularity with Basic at best... even though its grammatically incorrect, it can make good programs. proven millions time.
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
let x = 1;
(...)
let x = x + 1;
x := 1;
let y = 2*x + 1; // Could be ':=' here, open to discussion. Now 'y' holds '3'.
(...)
x := x + 1; // When this assignment is completed, 'y = 2*x+1' still holds, so 'y' now holds '5'.
how much time you "assign" vs time you "compare"? i think "assign" is much more used/typed... so assign using "=" and compare using "==" is much more sensibleIndeed. My Oberon code base shows ':=' (assign) is used 5 times more than '=' (compare). However, another factor to consider is the number of times a program is read rather than written. In my case this is often many thousands of times more.
IF a = b
a := b
how much time you "assign" vs time you "compare"? i think "assign" is much more used/typed... so assign using "=" and compare using "==" is much more sensible, that is C, and i dont have to keep pressing shift key so many times. https://www.jonathanshultsmd.com/blog/can-typing-cause-carpal-tunnel-syndrome (https://www.jonathanshultsmd.com/blog/can-typing-cause-carpal-tunnel-syndrome)
However, another factor to consider is the number of times a program is read rather than written. In my case this is often many thousands of times more.Irrelevant! As what we typed will not get translated/manifested into machine's language/code.. 'a = b' in c, and 'a := b' in pascal will end up as the same code in machine, interpreted/translated by c and pascal compiler respectively, compiler job is to bridge human friendly syntax into machine codes, the only difference is we have to type one more character in pascal..
I still haven't got the hang of how to read "=" and "==" in my head without it being awkward.because your mother language (pascal) clouded your mind, make you harder to get around another language, esp a language as good a c :P... if we just follow a language's notion and grammar, rather than our heart desire.. there should be no problem.. but then just as you pick your fav language.. i also did and abandon pascal grammar for the above logical reason, and some other technical reasons.
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yeah this is fine and a no-brainer. I don't really see a point of trying to invent something else. It just works.
I think that was discussed in older threads too. I don't really like the "let" keyword and it's not for parsing or typing reasons. I personally think "let" is ill-used for normal assignment, as to me it should not be used to change values of objects but only to define them as an invariant - that's how it's usually used in maths.
So if I see something like:Code: [Select]let x = 1;
(...)
let x = x + 1;
It doesn't look right to me.
IMHO, if we want to find a use for a "let" keyword, it should preferably be used as a invariant definition. For instance (types are omitted):Code: [Select]x := 1;
let y = 2*x + 1; // Could be ':=' here, open to discussion. Now 'y' holds '3'.
(...)
x := x + 1; // When this assignment is completed, 'y = 2*x+1' still holds, so 'y' now holds '5'.
This feature looks pretty cool to me. The "let" keyword may not be the best either and could be replaced by something else more explicit, such as just "invariant".
how much time you "assign" vs time you "compare"? i think "assign" is much more used/typed... so assign using "=" and compare using "==" is much more sensibleIndeed. My Oberon code base shows ':=' (assign) is used 5 times more than '=' (compare). However, another factor to consider is the number of times a program is read rather than written. In my case this is often many thousands of times more.
Beginner Pascal / Oberon programmers are taught to pronounce := as "becomes equals". ThenCode: [Select]IF a = b
is read as "If a equals b ..."
andCode: [Select]a := b
is read as "a becomes equal to b"
I still haven't got the hang of how to read "=" and "==" in my head without it being awkward.
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yup. Faster than to get used to than = and ==, and less prone to unnoticed errors.
The clearest assignment operator is, of course ← as in "x ← x + 1". Bonus points for recognising which language got that right many decades ago :)
As long as you don't have to give it a three-fingered salute, who cares whether one or two characters has to be typed. We're not using ASR33s anymore, are we mechatrommer?.
avg ← { (+ / ⍵) ÷ ⍴ ⍵ }
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yup. Faster than to get used to than = and ==, and less prone to unnoticed errors.
The clearest assignment operator is, of course ← as in "x ← x + 1". Bonus points for recognising which language got that right many decades ago :)
As long as you don't have to give it a three-fingered salute, who cares whether one or two characters has to be typed. We're not using ASR33s anymore, are we mechatrommer?.
Ahh APL, of which I have fond memories from my early 1980s mainframe days!
Here's a great sight (https://tryapl.org/) for playing with APL, such an expressive language, here's the definition of a function to calculate the average of any number of integers:Code: [Select]
avg ← { (+ / ⍵) ÷ ⍴ ⍵ }
Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yup. Faster than to get used to than = and ==, and less prone to unnoticed errors.
The clearest assignment operator is, of course ← as in "x ← x + 1". Bonus points for recognising which language got that right many decades ago :)
As long as you don't have to give it a three-fingered salute, who cares whether one or two characters has to be typed. We're not using ASR33s anymore, are we mechatrommer?.
Ahh APL, of which I have fond memories from my early 1980s mainframe days!
Here's a great sight (https://tryapl.org/) for playing with APL, such an expressive language, here's the definition of a function to calculate the average of any number of integers:Code: [Select]
avg ← { (+ / ⍵) ÷ ⍴ ⍵ }
I wasn't thinking of APL, since I'm not a fan and it hasn't been very influential. By and large APL seems to be a mistake carried through to perfection. There is one thing that APL did get right, the difference between "-3" and "¯3". But that is "correcting" a standard quirk with maths notation that requires every generation to learn about that mistake.
APL has formed the basis of, or influenced, the following languages:[citation needed]
A and A+, an alternative APL, the latter with graphical extensions.
FP, a functional programming language.
Ivy, an interpreter for an APL-like language developed by Rob Pike, and which uses ASCII as input.[46]
J, which was also designed by Iverson, and which uses ASCII with digraphs instead of special symbols.[7]
K, a proprietary variant of APL developed by Arthur Whitney.[8]
MATLAB, a numerical computation tool.[6]
Nial, a high-level array programming language with a functional programming notation.
Polymorphic Programming Language, an interactive, extensible language with a similar base language.
S, a statistical programming language (usually now seen in the open-source version known as R).
Speakeasy, a numerical computing interactive environment.
Wolfram Language, the programming language of Mathematica.[47]
No, I was thinking of a language that has been extremely influential and is better than quite a few of its successors. That language's syntax can be easily described with examples on a single sheet of A4 paper. Very few languages manage anything close.
Here's a great sight (https://tryapl.org/) for playing with APL, such an expressive language, here's the definition of a function to calculate the average of any number of integers:Code: [Select]
avg ← { (+ / ⍵) ÷ ⍴ ⍵ }
Here's a great sight (https://tryapl.org/) for playing with APL, such an expressive language, here's the definition of a function to calculate the average of any number of integers:Code: [Select]
avg ← { (+ / ⍵) ÷ ⍴ ⍵ }
You call that expressive, I call that a fantastic exercise in obfuscation.
Apart from the assignment symbol that almost anyone with a programming experience will get, the right hand part is undecipherable.
I'm under the impression again that you are conflating expressiveness with the least number of keystrokes possible.
function test binary(31)
declare a string
while true
a = a + 1
end
end
fonction test binaire(31)
déclarer a chaîne
pendant que true
a = a + 1
fin
fin
the fact is.. pascal is dead (technically), and you dont have anything to back it up, whether mechatrommers exist or not... whats grew on everybody today is Python, which uses the same assign and compare syntax as C... https://www.pythontutorial.net/python-basics/python-comparison-operators/ (https://www.pythontutorial.net/python-basics/python-comparison-operators/)Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yup. Faster than to get used to than = and ==, and less prone to unnoticed errors.
The clearest assignment operator is, of course ← as in "x ← x + 1". Bonus points for recognising which language got that right many decades ago :)
As long as you don't have to give it a three-fingered salute, who cares whether one or two characters has to be typed. We're not using ASR33s anymore, are we mechatrommer?.
the fact is.. pascal is dead (technically), and you dont have anything to back it up, whether mechatrommer exists or not... whats grew on everybody today is Python, which uses the same assign and compare syntax as C... https://www.pythontutorial.net/python-basics/python-comparison-operators/ (https://www.pythontutorial.net/python-basics/python-comparison-operators/)Pascal uses := for assignment and = for comparison. It took about 5 minutes to get used to this notation. I rather like it...
Yup. Faster than to get used to than = and ==, and less prone to unnoticed errors.
The clearest assignment operator is, of course ← as in "x ← x + 1". Bonus points for recognising which language got that right many decades ago :)
As long as you don't have to give it a three-fingered salute, who cares whether one or two characters has to be typed. We're not using ASR33s anymore, are we mechatrommer?.
Python relies on indentation to express containment/nesting, it is a horrible accident waiting to happen.learn from the lesson or else....
Python relies on indentation to express containment/nesting, it is a horrible accident waiting to happen.learn from the lesson or else....
(https://imgs.xkcd.com/comics/standards.png)
do you really want to reinvent ASR33's age ":="? ;D
I'm not sure what you mean by 'mother language' but Pascal certainly was not my 'first' language. I was taught FORTRAN IV at school in the 60's and ALGOL 60 at Uni in the early 70's and my programmer career started with Varian's TASC language.I still haven't got the hang of how to read "=" and "==" in my head without it being awkward.because your mother language (pascal) clouded your mind, make you harder to get around another language, esp a language as good a c :P... if we just follow a language's notion and grammar, rather than our heart desire.. there should be no problem.. but then just as you pick your fav language.. i also did and abandon pascal grammar for the above logical reason, and some other technical reasons.
The fun part is that the thread is starting to look like a popularity contest (which is what ends up happening in many programming language threads anyway) rather than stick to purely technical points. Which usually means game over, but I don't wanna ruin the thread. Good luck!That happened long ago. The thread OP is still, as of yesterday, hurling personal abuse and insults at those who disagree with HIS pet ideas.
What was PL/M like, and should it come back? I've never used it.
What was PL/M like, and should it come back? I've never used it.
As it looks like I'm the only one here who has used PL/M (for about 3 years) I'd put is somewhere between C and assembler. I still have a bit of a soft spot for it and would happily use it if a compiler were available.
I read above that PL/M is a stripped back PL/I. Having never looked at any PL/I source code until today I can see a vague passing resemblance but that might be the all-caps text fooling me.
PL/M source code here:
http://www.cpm.z80.de/source.html (http://www.cpm.z80.de/source.html)
It is written in Fortran 66.
What was PL/M like, and should it come back? I've never used it.
As it looks like I'm the only one here who has used PL/M (for about 3 years) I'd put is somewhere between C and assembler. I still have a bit of a soft spot for it and would happily use it if a compiler were available.
I read above that PL/M is a stripped back PL/I. Having never looked at any PL/I source code until today I can see a vague passing resemblance but that might be the all-caps text fooling me.
I never used PL/M myself but have been looking at it closely recently, it was - at least initially - closely allied with the 8080, the language itself had facilities for interacting with DMA devices too it seems, may I ask, what kind of work did you do with PL/M?
What was PL/M like, and should it come back? I've never used it.
As it looks like I'm the only one here who has used PL/M (for about 3 years) I'd put is somewhere between C and assembler. I still have a bit of a soft spot for it and would happily use it if a compiler were available.
I read above that PL/M is a stripped back PL/I. Having never looked at any PL/I source code until today I can see a vague passing resemblance but that might be the all-caps text fooling me.
I never used PL/M myself but have been looking at it closely recently, it was - at least initially - closely allied with the 8080, the language itself had facilities for interacting with DMA devices too it seems, may I ask, what kind of work did you do with PL/M?
We were doing remote control and monitoring of industrial equipment.
8085 processor, 8259 interrupt controllers, 8237 DMA controllers, 8251 UARTs, 8274 HDLC/SDLC controllers, plus RAM and EPROM on the processor board. The 8251s talked in/out to multiple serial ports. The 8274 implemented an 800kbps interboard comms link. All running under DMA to reduce CPU usage.
8031 processor (don't ask) and a TMS9918 (?) VDP on the graphics board, commands via a 8274 on the interboard comms link. Also using PL/M (PL/M-51).
8751 (old top, UV window) in the remote units with analogue inputs, digital outputs via relays, and digital inputs.
The fun part is that the thread is starting to look like a popularity contest (which is what ends up happening in many programming language threads anyway) rather than stick to purely technical points. Which usually means game over, but I don't wanna ruin the thread. Good luck!
{
"en": {
"IsNotAKeyword": "0",
"Argument": "argument",
"Binary": "binary",
"Boolean": "boolean",
"Builtin": "builtin",
"By": "by",
"Call": "call",
"Coroutine": "coroutine",
"Decimal": "decimal",
"Declare": "declare",
"End": "end",
"Enum": "enum",
"Fixed": "fixed",
"Float": "float",
"Function": "function",
"Goto": "goto",
"If": "if",
"Internal": "internal",
"Interrupt": "interrupt",
"Language": "lingua",
"Loop": "loop",
"Namespace": "namespace",
"Out": "out",
"Private": "private",
"Procedure": "procedure",
"Public": "public",
"Readonly": "readonly",
"Ref": "ref",
"Return": "return",
"Returnon": "returnon",
"Singlet": "singlet",
"Static": "static",
"String": "string",
"Structure": "structure",
"To": "to",
"Type": "type",
"Until": "until",
"Using": "using",
"While": "while",
"Varying": "varying",
"Yield": "yield",
"Arg": "arg",
"Bin": "bin",
"Dcl": "dcl",
"Dec": "dec",
"Func": "func",
"Proc": "proc",
"Struct": "struct"
},
"fr": {
"IsNotAKeyword": "0",
"Argument": "argument",
"Binary": "binaire",
"Boolean": "booléenne",
"Builtin": "intégré",
"By": "by",
"Call": "appeler",
"Coroutine": "coroutine",
"Decimal": "décimal",
"Declare": "déclarer",
"End": "fin",
"Enum": "enum",
"Fixed": "fixe",
"Float": "flottant",
"Function": "fonction",
"Goto": "goto",
"If": "si",
"Internal": "interne",
"Interrupt": "interrompre",
"Language": "lingua",
"Loop": "boucle",
"Namespace": "namespace",
"Out": "depuis",
"Private": "privé",
"Procedure": "procédé",
"Public": "public",
"Readonly": "readonly",
"Ref": "ref",
"Return": "retour",
"Returnon": "retour si",
"Singlet": "singlet",
"Static": "static",
"String": "chaîne",
"Structure": "structure",
"To": "to",
"Type": "type",
"Until": "avant",
"Using": "using",
"While": "tandis que",
"Varying": "varying",
"Yield": "donner",
"Arg": "arg",
"Bin": "bin",
"Dcl": "dcl",
"Dec": "déc",
"Func": "fonc",
"Proc": "proc",
"Struct": "struct"
}
What was PL/M like, and should it come back? I've never used it.
As it looks like I'm the only one here who has used PL/M (for about 3 years) I'd put is somewhere between C and assembler. I still have a bit of a soft spot for it and would happily use it if a compiler were available.
I read above that PL/M is a stripped back PL/I. Having never looked at any PL/I source code until today I can see a vague passing resemblance but that might be the all-caps text fooling me.
I never used PL/M myself but have been looking at it closely recently, it was - at least initially - closely allied with the 8080, the language itself had facilities for interacting with DMA devices too it seems, may I ask, what kind of work did you do with PL/M?
PL/I was a designed, specified programming language. It was created after a detailed analysis of assembler, Cobol, Algol and Fortran. It had rich bit and packing/alignment features due to the cost of memory. Many programmers back then (1960s) had solid assembler experience and were no strangers to packing data into fields to conserve memory, so it was high on the list of the designers to give the PL/I programmer a similar ability to work at that level, this are received a lot of attention - all these years later that same mindset could apply to resource constrained MCUs.
It was a huge success and saw huge uptake in UK and Europe as well as Eastern block countries, it was marketed there differently to the US, where the costs of the language were rather high and this perhaps the biggest reason it became less well known over here. My first real programming job was a trainee programmer on PL/I on IBM mainframes in Enfield, London. Later I was hired to work in the City of London because Stratus hardware fault tolerant computer had started to see like hot cakes in mission critical environments (stock exchanges, investment banks, market makers). Stratus was interesting and had an OS written in PL/I subset G and was the preferred language to use on these machines, PL/I programmers were thus in demand and I got a job on that basis. The CTO of Stratus was interesting too, had a business developing and selling compilers (including PL/I) for Burroughs, DEC, Data General, Prime etc.
For those interested in computer history here's an interview with him (https://archive.computerhistory.org/resources/access/text/2013/03/102743157-05-01-acc.pdf).
I've been particularly concerned recently with the grammar for a new language, once a grammar is defined there's no going back, one must live with the consequences of the choice for the lifetime of the language, PL/I's grammar is not perfect but it is perhaps one of the best imperative language grammars around IMHO.
I also developed a Subset G PL/I compiler during the 90s so I have solid experience of this, that compiler was never strictly completed but did generate linkable runnable COFF object modules for Windows NT, I wrote the entire compiler (except the small bootstrap start code which was assembler) in C, initially Borland then later Visual C, writing a compiler for PL/I in C was a unique learning experience, I saw the good. the bad and the ugly of both languages, up close, this was all before the internet too, so all research and design was driven by books and histiorical reprints of articles and papers from my local library.
Coral 66 is a general-purpose programming language based on ALGOL 60, with some features from Coral 64, JOVIAL, and FORTRAN. It includes structured record types (as in Pascal) and supports the packing of data into limited storage (also as in Pascal). Like Edinburgh IMP it allows embedded assembler, and also offers good run-time checking and diagnostics. It is specifically intended for real-time and embedded applications and for use on computers with limited processing power, including those limited to fixed point arithmetic and those without support for dynamic storage allocation.
The language was an inter-service standard for British military programming, and was also widely adopted for civil purposes in the British control and automation industry. It was used to write software for both the Ferranti [2] and GEC computers from 1971 onwards. Implementations also exist for the Interdata 8/32, PDP-11, VAX, Alpha platforms and HP Integrity servers; for the Honeywell, and for the Computer Technology Limited (CTL, later ITL) Modular-1; [3] as well as for SPARC running Solaris and Intel running Linux.
A variant of Coral 66 was developed during the late 1970s/early 1980s by the British GPO, in conjunction with GEC, STC and Plessey, for use on the System X digital telephone exchange control computers, known as PO-CORAL. This was later renamed BT-CORAL when British Telecom was spun off from the Post Office. Unique features of this language were the focus on real-time execution, message processing, limits on statement execution between waiting for input, and a prohibition on recursion to remove the need for a stack.
As Coral was aimed at a variety of real-time work, rather than general office DP, there was no standardised equivalent to a stdio library. IECCA recommended a primitive I/O package to accompany any compiler (in a document titled Input/Output of Character data in Coral 66 Utility Programs). Most implementers avoided this by producing Coral interfaces to existing Fortran and, later, C libraries.
Perhaps CORAL's most significant contribution to computing was the enforcement of quality control in commercial compilers. [3] To have a CORAL compiler approved by IECCA, and thus allowing a compiler to be marketed as a CORAL 66 compiler, the candidate compiler had to compile and execute an official suite of 25 test programs and 6 benchmark programs. The process was part of the BS 5905 approval process. This methodology was observed and adapted later by the United States Department of Defense for the official certification of Ada compilers.
def header struct order (auto)
...
end
def header struct order (explicit)
...
end
def header struct
length bin(16)
code byte(1)
padding(1)
type bin(32)
end
def header struct aligned
length bin(16)
code byte(1)
type bin(16)
end
Alignment and packing can be defined in many programming languages these days. Even C. This is certainly useful but nothing new. Ada is by far the most flexible for this.
Automatic ordering of members is rarer - well, some languages do not define a specific order for "structures", so compilers are allowed to reorder the members as they see fit. Not particularly new either, but indeed not in C or C++. While not mind-blowingly useful, that would give opportunities for optimizations when the developer doesn't care about order, which is relatively frequent indeed unless you map structs to low-level objects.
def message struct
sequence bin(31)
tag byte(2)
// Note all members that are padding/ignored must have same name within the struct
XXXX byte (5) padding
XXXX byte (3) ignored // ignore the 3 byte checksum
end
The fun part is that the thread is starting to look like a popularity contest (which is what ends up happening in many programming language threads anyway) rather than stick to purely technical points. Which usually means game over, but I don't wanna ruin the thread. Good luck!
It seems you are a French speaker, so on that basis let me ask you a question, how would you update this JSON's "fr" entry to use the most appropriate French terms for the various keywords:
(...)
The fun part is that the thread is starting to look like a popularity contest (which is what ends up happening in many programming language threads anyway) rather than stick to purely technical points. Which usually means game over, but I don't wanna ruin the thread. Good luck!
It seems you are a French speaker, so on that basis let me ask you a question, how would you update this JSON's "fr" entry to use the most appropriate French terms for the various keywords:
(...)
I missed this thread which is why I didn't answer. Note that I don't think translating programming language keywords is ever a good idea (it has been done before, and it eventually died.)
But anyway, I'll have a look at the list if I get the time.
"fr": {
"Boolean": "booléen", -- more neutral form, 'booléenne' is the feminine version, although feminazis may not like the neutral form, which is masculine
"Builtin": "intégré", -- this one doesn't sound quite right to me (even if the general meaning is correct), but I don't really know how to translate it better without more context about what 'builtin' would mean in said prog. language
"By": "par",
"Goto": "aller à/en",
"Interrupt": "interruption",
"Language": "langage",
"Namespace": "espace de noms", -- which is correct but frankly looks odd and overly verbose
"Out": "sortie",
"Procedure": "procédure",
"Readonly": "lecture seule",
"Singlet": "singleton", -- if by 'singlet' you mean a set or list of 1 element
"Static": "statique",
"To": "à",
"Until": "jusqu'à ce que",
"Using": "utilisant",
"While": "tant que",
"Varying": "variant",
"Yield": "céder",
}
I would make the following changes: (only kept what needs changed here)"fr": {
"Boolean": "booléen", -- more neutral form, 'booléenne' is the feminine version, although feminazis may not like the neutral form, which is masculine
"Builtin": "intégré", -- this one doesn't sound quite right to me (even if the general meaning is correct), but I don't really know how to translate it better without more context about what 'builtin' would mean in said prog. language
"By": "par",
"Goto": "aller à/en",
"Interrupt": "interruption",
"Language": "langage",
"Namespace": "espace de noms", -- which is correct but frankly looks odd and overly verbose
"Out": "sortie",
"Procedure": "procédure",
"Readonly": "lecture seule",
"Singlet": "singleton", -- if by 'singlet' you mean a set or list of 1 element
"Static": "statique",
"To": "à",
"Until": "jusqu'à ce que",
"Using": "utilisant",
"While": "tant que",
"Varying": "variant",
"Yield": "céder",
}
Some keywords are clunky to translate, and I don't think it's a good idea as I said (and MIS42N said as well.) But there you go.
Do you know how much i hate that i have to translate excel/calc functions because one colleague's pc is set to italian locale and my pc is set to english locale?
Translatable keywords is as bas as no reserved words
And there is nothing bad in having reserved words, even if they are not in your native language. I remember it took me a time to understand why word "x" was chosen. After all in middle school we were learning programming alongside english. But it didn't really matter. I only knew i had to follow the syntax
Now back to me, the language designer working on v 2.0, in this new version we are seeking to add a new keyword to represent asynchronous IO and choose the keyword "async" what will happen when we compile your code, using v2.0 (where "async" is now a new reserved word)?The most common initial complaint I hear from newcomers to languages like Oberon and Modula-2 are their use of case-sensitive identifiers and all-uppercase reserved words.
Now back to me, the language designer working on v 2.0, in this new version we are seeking to add a new keyword to represent asynchronous IO and choose the keyword "async" what will happen when we compile your code, using v2.0 (where "async" is now a new reserved word)?The most common initial complaint I hear from newcomers to languages like Oberon and Modula-2 are their use of case-sensitive identifiers and all-uppercase reserved words.
However, a significant benefit of these features are that name clashes of this sort never happen - unless you are in the habit of using all-uppercase identifiers in your own code. You can also freely use lowercase or mixed-case identifiers in your own code without ever having to think about existing reserved words.
if you have existing code base you have to mantain, you don't have to suddntly use v2.0 in that project if you don't need features from 2.0, there are many compilers that give an option to use a certain version of the language
Thinking about it.. You know one feature that C doesn't really have but that i would like to have in a language?
I would like to have such options to be present in a source file:
The reason is, let's say i want this particular code file to compile with a specific version of the language, or with this specific option
so that i don't have to remember to do it on every project
Also would be easier to document
Thinking about it.. You know one feature that C doesn't really have but that i would like to have in a language?
I would like to have such options to be present in a source file:
The reason is, let's say i want this particular code file to compile with a specific version of the language, or with this specific option
so that i don't have to remember to do it on every project
Also would be easier to document
Seems nice on surface, but it's a good idea to write a makefile and make that part of your process, i.e., put it in revision control system and mentally treat it as something equally important to all those source files.
You can totally generate different rules (command line options) in makefile on a source file basis. The good side compared to your suggestion is, you don't have to copy-paste the settings to each source file and hope they are kept in sync, because in realistic projects, 99% of files use the same settings 99% of the time, so good to have in one file (makefile).
If you have difficulty remembering the "good" settings, just copy the whole makefile from the previous project - or make yourself a good template. This is what I do.
If you have difficulty remembering the "good" settings, just copy the whole makefile from the previous project - or make yourself a good template. This is what I do.
but then i have to remember to do it every time i use that source file. The point is to not having to remember stuff
but then i have to remember to do it every time i use that source file. The point is to not having to remember stuffI keep stuff in units; currently one directory per unit test/experiment/detail. If a file requires special compiler options, or I mix C and C++ (for example because g++ still doesn't support named address spaces, unlike e.g. clang), I document it. I have a short comment in the Makefile, plus a README in that directory.
I keep stuff in units; currently one directory per unit test/experiment/detail. If a file requires special compiler options, or I mix C and C++ [..] I document it. I have a short comment in the Makefile, plus a README in that directory.
call = 1234
goto done
ball = 1234
proc revalidate() { }
call ball()
goto = 999
procedure revalidate() { }
proc = 999;
call = 1234;
goto done;
ball = 1234;
proc revalidate()
{
;
}
call ball();
goto = 999;
procedure revalidate()
{
}
proc = 999;
ptr->xyz = 2;
proc call ()
{
call goto();
proc inner()
{
do_stuff = 0;
}
}
If you have difficulty remembering the "good" settings, just copy the whole makefile from the previous project - or make yourself a good template. This is what I do.
but then i have to remember to do it every time i use that source file. The point is to not having to remember stuff
You lost me. Remember to do what exactly? Copying one makefile is too much to remember?
I think the opposite is true. If the compiler options, language standards etc. are specified in source files directly, that's a lot to remember, on every file! I rather copy one file than copy-paste 10 lines of configuration in each project source file.
Ecosystem is important. The good feature in C ecosystem is, language versions are actually standardized, and compilers target specific versions (usually chosen by a command line flag).
counter = 100;
/* declare some variables named the same as keywords */
dcl dcl dec based(counter); /* dcl is a keyword ! */
dcl counter binary;
declare goto binary; /* goto is a keyword ! */
goto call; /* call is a keyword ! */
call declare(); /* declare is a keyword */
call declare (1,2,3);
return (123);
if if = then then
dcl realm binary;
call reboot(0);
go to end;
end;
if if = then then
dcl realm binary;
call reboot(0);
go to end;
else
return (123);
if = 44;
end = then + else;
end;
I can feel the power...Code: [Select]if if = then then
dcl realm binary;
call reboot(0);
go to end;
else
return (123);
if = 44;
end = then + else;
end;
(Attachment Link)
There are various reserved words in the C language, such as 'if'. I suspect, it is because it is known and learnt from experience, that if you don't have enough (of the right kind of) reserved words. It can lead to too many ways in which programmers can accidentally introduce faults in the source code. Which (due to the lack of reserved words), can make the compiler, incorrectly ignore without generating any warnings or error messages, mistake(s) in the source code.
Also it can lead to valid source code, which is especially confusing for the unwary programmer.
C is not the best of examples, for my recent posts in this thread. Because its #define concept, relies on programmer discipline, to not inappropriate use it, to make significantly poor (judgement) quality source code, which is for example, very difficult to understand by anyone, other than the original programmer.
void f(char *x, char*y, int z)
{
int z2 = (z + 7) / 8;
switch (z % 8)
{
case 0:
do
{
*x++ = *y++;
case 7:
*x++ = *y++;
case 6:
*x++ = *y++;
case 5:
*x++ = *y++;
case 4:
*x++ = *y++;
case 3:
*x++ = *y++;
case 2:
*x++ = *y++;
case 1:
*x++ = *y++;
} while (--z2 > 0);
}
}
I can feel the power...Code: [Select]if if = then then
I can feel the power...Code: [Select]if if = then then
And I guess this is the first crucial step to enable your embedded friends write cycle-accurate code for PIC microcontrollers, yeah?
Why not limit the keywords to being 6 pseudo-variables and nothing else? That allows you to have whatever control structures are suited to your requirements.
That's proven to be successful in embedded instruments manufactured by HP and Tektronix, e.g. protocol analysers and oscilloscopes and maybe more. I believe it to be significant that they were produced by hardware engineers (not software engineers) that only cared about how a language behaved and could achieve easily, and didn't care about the language itself.
The 6 words were/are: true, false, nil, self, super, thisContext.
Not only cycle accurate, but extremely easy to understand and maintain, not like the *x++ = **t-- C horror ?
Not only cycle accurate, but extremely easy to understand and maintain, not like the *x++ = **t-- C horror ?
Think about it - in C, ***************************x is a valid expression, C bad.
"if if then if else then if if if if if then then if if if else then" is much better!
Not only cycle accurate, but extremely easy to understand and maintain, not like the *x++ = **t-- C horror ?
Think about it - in C, ***************************x is a valid expression, C bad.
"if if then if else then if if if if if then then if if if else then" is much better!
Think about it - in C, ***************************x is a valid expression, C bad.
"if if then if else then if if if if if then then if if if else then" is much better!
Except of course that the latter is syntactically wrong, illegal, gibberish; whereas the former is valid, legal C gibberish!
if if else then
else if else then else
then then =
if then then
Think about it - in C, ***************************x is a valid expression, C bad.
"if if then if else then if if if if if then then if if if else then" is much better!
Except of course that the latter is syntactically wrong, illegal, gibberish; whereas the former is valid, legal C gibberish!
What, your language uses whitespace as a syntactic separator? Why would you do that, isn't that limiting? What if the user wants to have spaces in the variable names, or what if you later find out you want keywords with spaces? I just wanted to have a variable named "if then if else then if if if if if then then if if if else" and you call it gibberish. Or wait, no, actually I want it with line feeds in variable names and all, how about:Code: [Select]if if else then
else if else then else
then then =
if then then
Clearly the language isn't powerful enough, yet! But I'm sure you'll get there.
Language v1.0 reserved words = if then else call end
Language v1.1 reserved words = if then else call end goto return
Language v2.0 reserved words = if then else call end goto return interrupt coroutine allocate
Language v3.4 reserved words = if then else call end goto return interrupt coroutine allocate lock trap retry
Does this make it clearer? We simply do not know what new features/keywords might get added in each future version of a language. Therefore if we add to the list of reserved words with each version we will eventually break older code somewhere, a v1.0 source file in part of a library, might have used "lock" as a procedure or function name, but refuses to compile under v3.4
There is nothing to stop us though from issuing style notices, or optionally telling a compiler (via some option) to act as if keywords are reserved and report errors, that's fine, that's a teams conscious choice though not an imposition by the language.
Being able to say "we might add zero or more new language keywords in each future version but guarantee that your existing source code will always compile, your code is guaranteed to compile under every future language version".
Names also must be explicitly declared, so unless one declares "if' and "then" and "else" there cannot be accidental confusion, the examples I've shown are test cases, proof that the grammar definition is sound, contrived examples used to test the grammar and parser, nothing more.
Reserved words are "training wheels", handcuffs to the language designers, anyone can write confusing looking code in any language, but that's their call, if they want to do it then that's their choice, C has plenty of reserved words but still let's us write insane looking code, keyword reservation is old fashioned thinking, time to move on.
Read this https://doc.rust-lang.org/reference/keywords.html
Yes, that does make it clearer. I (also, if you do as well), also have much respect for Rusts, concerns and attempts/features, which attempts to take responsibility, for not only current source code, but older source code as well. I.e. it hopes to allow current (or older) source, code to remain compilable/compatible with new standards. E.g. By allowing specific command version numbering syntax, to help with that process.
I think as already mentioned in this thread. There is a fundamental problem, with new programming languages, in general. They tend to have a fair amount of changes, in the early releases, as there is potentially much refining, polishing and fine honing/tuning of the language. In the light of experience, and as more users, join the increasing family of people, using the new language.
I'm glad, you, early on are taking much thought and consideration, into this (apparently) important issue, with programming language design.
But there are other solutions. Another would be to start off with a completed, reserved word list, which is set in stone. Call it the 'hard' reserved word list.
For future releases of the language, any additions to the reserved list, can only be added to a 'soft' reserved word list. The 'soft' version, unlike the 'hard' version, CAN BE over-ridden by the users of the compiler. Unlike the original 'hard' reserved word list.
That way, you could get to eat your cake, and keep it.
Also, any obsolete 'hard' reserved words, could, instead of deleting them, be moved, at least for a long while, into the 'soft' reserved word list.
'soft' reserved words, could still produce a compiler warning message. With options to either treat it as an error, or ignore it completely. As compiler flag options. Then users could choose, how they want to treat the various reserved word lists.
Some languages, seem to use additional syntax, to allow many (rare) reserved words (or hidden variables/features, link/compiler information etc). But because of the syntax, they will tend to never clash with programming source code.
E.g. using '_errorNumber' and '_errorDesription' to find out the compilers last run time, error codes/messages. As they are less likely to be chosen names in the compiler, compared to e.g. 'error'.
E.g. Prefixing new words with an underscore or two. '_start' or '__start', or ending with a '!'. Etc.
Some languages (Python, I'm looking at you), change so much between, e.g. 2.0 and 3.0, that to all intents and purposes, it is a new and/or completely different (and relatively incompatible) language. So, this does seem to be a REAL issue.
Even some/many frameworks/libraries and things, can change so much, between major revision numbers. That there is massive incompatibilities, between them. With various negative consequences, such as programmers who were familiar with (e.g. GTK2, compared to GTK3, and then GTK4), having later version(s), being significantly different, so they effectively have to re-learn it and rewrite/modify lots of code, in some cases.
It can annoy/alienate some/many people, so that they end up turning their backs on such systems.
yield = ((total - defects) / total) * 100;
Then one day you upgrade to v3.4 of newlang only to find your code no longer compiles because newlang now supports iterators and 'yield' can no longer be used that way! Right away we must change code, run unit test, create commits, review code and so on, a total waste of people's valuable time all imposed by a language because the designers didn't think about backward compatibility.
It is not just that. When such changes occur, significantly in the future. The original programmers, may be unavailable (busy on other projects), or have left the company. Even if available, it might be many years ago, when they wrote that code, so they have totally forgotten about it.
So, for them (or others), to now go back, and rework the source code. Can be a rather time-consuming, problematic (further new bugs might be introduced, because they no longer understand the code), and an expensive loss of (valuable/limited) resources, that could be better spent, doing other useful things.
It might even be someone, trying to recompile the software, and sort out any dependencies, just so they can use it, some time in the future. So if the (then) current V3.4, no longer compiles, without many fatal errors, using the existing source code base (which could be open source or commercial). That will put a real damper, on the activities, within the new language.
Some (very successful and popular) programming languages, have such huge existing software bases, in source format. That making future changes to the language, without breaking any existing functionality. Can be a real-minefield.
Taking CPU instruction sets, as an analogy. On the one hand, the X86 / X86-64 instruction sets, have amazing compatibility, even going back around 40 years, to the original 8086/88 CPUs. But some people think, that it also causes big baggage (like) problems. Whereas alternative architectures, such as Arm, tend to be rewritten from scratch, only going back one or two instruction sets, as regards compatibility. But that potentially allows the instruction set to move on from the old days, and soak up, any modern CPU performance improvements, with minimal handicapping, from old, somewhat obsolete instruction sets, of the distant past.
E.g. How many people, really run native 8088 programs, on their 192 core (dual socket 2 x 96 core), AMD Epic processors?
I have heard some of the backward compatibility, may be no longer available, but I'm not sure. Removing it has been discussed, but I'm not sure if it happened or not.
Windows, seems to happily drop support, for even rather modern/recent processors. But that could be, because they get paid license fees, mainly when new PCs are purchased, so it is in their interests, to obsolete CPUs, even if they are rather recent ones.
In other words. It is annoying when a programming language, introduces, show breaking changes, between say versions 2.0 and 3.0 . But, that might (or might not), be worth it, from a longer time scale point of view.
E.g. Despite Python, sometimes dramatically changing, between major release number versions. It does seem to still be very popular. So I wouldn't like to say, one method is best. It is a tricky situation to handle.
As they say, you can't make a omelette without breaking eggs. Maybe such dramatic changes, is ok, if done responsibly, and only very rarely. Such as once every 7 or 8 years.
If you DON'T allow show stopping changes, ever. Eventually the thing (a programming language in this case), can eventually become stale and fixed. Unable to move forward, with any/much momentum. Ultimately, risking the language becoming obsolete (i.e. people stop using it), as time goes on.
If you are still primarily aiming this to be an MCU language. There is a tendency for such projects, to have relatively short development time-scales (but, they COULD be very long, it depends). With whatever language version, they started out with, on that particular MCU, remaining, throughout the project life-time.
Because if you have MCU (embedded) software, which works well and is considered bug free. Made with version 1.24 of the compiler, nine years ago.
Experience tends to find, that if you update the compiler to the latest version 9.79, on a much later version of windows. There is a tendency, for various bugs (that didn't exist previously), to suddenly appear, mismatches within the new (updated) libraries and even entire functionality, that is no longer present in the latest libraries that come from the manufacturer.
The latest compiler, may not even work, with your MCU, from many years ago.
If it ain't broke, don't mess with it.
With FPGAs, and older specific FPGAs, typically. You can't even use, older FPGAs, on the latest FPGA software packages, as support was dropped for those older FPGAs, many years/versions ago.
You started off this thread with mention of PIC MCUs. Which tend to lose support for the older PIC models, on the latest programmer-devices/compilers and libraries. So, total backward compatibility, may not be necessary or even a good idea. I suppose care/caution is needed.
Also remember the K.I.S.S. (Keep It Simple Silly/stupid), principal. Making the compiler too overly complicated, with way too much functionality. May mean that, it can never be completed, given its available resources (such as programming team size/time, for creating the compiler). Sometimes, you have to harshly remove extra functionality, to make things practicable.
Imagine you were calculating the yield of some process:Code: [Select]
yield = ((total - defects) / total) * 100;
Then one day you upgrade to v3.4 of newlang only to find your code no longer compiles because newlang now supports iterators and 'yield' can no longer be used that way! Right away we must change code, run unit test, create commits, review code and so on, a total waste of people's valuable time all imposed by a language because the designers didn't think about backward compatibility.
(See, this is the difference between real and made-up problems. Real problems can be difficult, but made-up problems are ridiculous when you look at them closely.)
Variable renaming is simple work, you don't need the original developer to do it.
If you are changing to different language version, because you want to use new features (so you will be using the new features, and modifying code, or writing new code), a simple variable renaming is going to be smallest of your problems. You can't avoid unit-testing and testing the whole thing with all the changes.
And if you are not going to make any changes, why change the language version then?
(See, this is the difference between real and made-up problems. Real problems can be difficult, but made-up problems are ridiculous when you look at them closely.)
whateverImagine you were calculating the yield of some process:Code: [Select]
yield = ((total - defects) / total) * 100;
Then one day you upgrade to v3.4 of newlang only to find your code no longer compiles because newlang now supports iterators and 'yield' can no longer be used that way! Right away we must change code, run unit test, create commits, review code and so on, a total waste of people's valuable time all imposed by a language because the designers didn't think about backward compatibility.
less than two minutes to fix this :-DD
Variable renaming is simple work, you don't need the original developer to do it.
If you are changing to different language version, because you want to use new features (so you will be using the new features, and modifying code, or writing new code), a simple variable renaming is going to be smallest of your problems. You can't avoid unit-testing and testing the whole thing with all the changes.
And if you are not going to make any changes, why change the language version then?
(See, this is the difference between real and made-up problems. Real problems can be difficult, but made-up problems are ridiculous when you look at them closely.)
its called delusion... and i dont get this "bit field support" delusion in newer languague. C already support it, its the job of compiler to make best use of any available machine specific codes for it.
Assembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
Well, I'm a simple down to earth guy, not an architecture astronaut, so if some one has time, skills and interest in improving a language, why not not get a pre-existing C compiler, a simple version like TinyCC, or LCC, or even SDCC and implement some of the proposed feature in the first posts instead of going into demented syntax and paradigms from long lost languages suitable for long lost platforms and that they were proved hard to use and mostly useless at the time when they were hot and teached in schools and universities.
You know these paradigms that promise that if you throw away all your experience and way of thinking, followed by all previous source code and libraries, you'll get some new and vastly improved way of doing things, one has only to re-learn everything and sky is the limit. This (IMHO) failed before and will fail now as well.
Instead let's implement the low hanging fruits properly in C, I'l put here the low-hanging fruits that I would like to have in a C compiler (extension) and that doesn't need a new grammar or crazy syntax:
- binary representation of numbers: make 0y11_101_1_00 possible, with _ as group separator to make life easier, add some common sense rules of filling values depending of the left size and throwing warning or errors if there are more bits in the source as in the destination,
- introduce a:
#pragma cycle_critical start <max cycle number>
code..
#pragma cycle_critical stop
to enable sections of code that are critical, if the back-end knows how to generate such a code it will do best effort to fit the code in the number of given cycles, or spit out warnings/errors like: "WARNING: Implementation possible in XXX cpu cycles, offered YYY...", if not it will be just ignored (maybe with warnings) to keep compatibility.
- computed gotos, here I don't know how to offer a proper syntax, maybe use @labels everywhere and put them in an enum/pointers array or something that can be used for calculations ?
If somebody will be able to implement this in a sane way in one of the existing C/C++ compilers suitable for MCUs (see above) it will definitely get my full respect, maybe even some shekels if it works nicely and the result will even have a chance to be used by more than two people ^-^. Otherwise I feel the will be yet another implementation of brainfuck.
Just my two humble € cents, now three fiddy because of inflation.
Cheers,
DC1MC
C++03's parser defines “>>” as the right shift operator or stream extraction operator in all cases. However, with nested template declarations, there is a tendency for the programmer to neglect to place a space between the two right angle brackets, thus causing a compiler syntax error.
List<Frame<int> > items;
no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
if some one has time, skills and interest in improving a language, why not not get a pre-existing C compiler
no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
can you publish for discussion? its much better than discussing uthopia. keep re-stating something secret will not do any good, similar to uthopia story..if some one has time, skills and interest in improving a language, why not not get a pre-existing C compilerThat's precisely what I did with my-c :D
So, it can be done!
and havent you got the idea yet? its a 1 to 1 mapping from brainf*ck grammar to at least something human can cope... so instead of typing 0x0001F 10E, we can type MOV REG1 for example.It's a language too, assembler can do anything (and more) that C can do.no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
Unlike Java, which has a special operator >>> for logical shifting apart from the usual >> and <<, C and C++ have only arithmetic shifting with some areas left undefined and implementation-defined.
and havent you got the idea yet? its a 1 to 1 mapping from brainf*ck grammar to at least something human can cope... so instead of typing 0x0001F 10E, we can type MOV REG1 for example.It's a language too, assembler can do anything (and more) that C can do.no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
there is difference between human language vs machine language... the purpose of the former is to fill every gap way down to the later. the way i see whenever anyone want to create a new language, its the human (friendly) language, a higher level one... but if what you are after is brainf*ck language, then by all means go for it, i've provided a link for you earlier to win the contest... just make it clear to us..Yes I know that, but it is a language isn't it? are you claiming it's not a language? has no syntax rules? are you saying that?and havent you got the idea yet? its a 1 to 1 mapping from brainf*ck grammar to at least something human can cope... so instead of typing 0x0001F 10E, we can type MOV REG1 for example.It's a language too, assembler can do anything (and more) that C can do.no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
there is difference between human language vs machine language... the purpose of the former is to fill every gap way down to the later. the way i see whenever anyone want to create a new language, its the human (friendly) language, a higher level one... but if what you are after is brainf*ck language, then by all means go for it, i've provided a link for you earlier to win the contest... just make it clear to us..Yes I know that, but it is a language isn't it? are you claiming it's not a language? has no syntax rules? are you saying that?and havent you got the idea yet? its a 1 to 1 mapping from brainf*ck grammar to at least something human can cope... so instead of typing 0x0001F 10E, we can type MOV REG1 for example.It's a language too, assembler can do anything (and more) that C can do.no! assembly is 1 to 1 map to machine code, no need to translate in any fancier way. assembler is just that, 1 to 1 mapping.. duh! how you can make such conclusion?QuoteAssembly language already support it, its the job of assembler to make best use of any available machine specific codes for it.
...snip..in the end... your brainf*ck language will get eventually be translated into machine codes anyway... that turing complete machine?... like all other programming languages in existance. you want bit type and string support? go for it! it will get introduced into bloatness of machine codes anyway... like most of the rest (Basic, Pascal etc)... and sorry, i'm not further discussing since its clear what you re after now (brainf*ck language however powerful it is) and my first reply has become more and more apparent (getting worse actually). it has no practical use for me... if you've made it clear since your first post, i would've not make the first post, not even a glimpse. but the ship had sail, this thread will be a curse for me appearing whenever i hit "Show new replies to your posts". tldr.... only one thing i ask from you.... please make it short! pleaseeee! ;D (ps: i have another 1 or 2 never ending cursed threads going on that keep posting no practical knowledge to me)... cheers.
So assembler is a language then, good we agree on that. Now can the C language do anything that assembly language can't? You want to talk about languages, lets talk...
...snip..in the end... your brainf*ck language will get eventually be translated into machine codes anyway... that turing complete machine?... like all other programming languages in existance. you want bit type and string support? go for it! it will get introduced into bloatness of machine codes anyway... like most of the rest (Basic, Pascal etc)... and sorry, i'm not further discussing since its clear what you re after now (brainf*ck language however powerful it is) and my first reply has become more and more apparent (getting worse actually). it has no practical use for me... if you've made it clear since your first post, i would've not make the first post, not even a glimpse. but the ship had sail, this thread will be a curse for me appearing whenever i hit "Show new replies to your posts". tldr.... only one thing i ask from you.... please make it short! pleaseeee! ;D
So assembler is a language then, good we agree on that. Now can the C language do anything that assembly language can't? You want to talk about languages, lets talk...
i'll be here! ;D i thought you are the one asking for discussion? instead of posting proposal thats becoming more and more resembling Brainfuck (https://en.wikipedia.org/wiki/Brainfuck) or just talk like...
https://www.youtube.com/watch?v=yL_-1d9OSdk (https://www.youtube.com/watch?v=yL_-1d9OSdk)
at least you can relieve big stress out of people... ;D
..but some others have not and prefer to object to the sheer effrontery in even hinting that anything better than C is possible.maybe for your own good? certainly anything is not impossible, we just open to that, but we dont know how.. since you think you can do it, just prove it in short possible time, not suggesting far more worse example from your wonderland delusion.
..but some others have not and prefer to object to the sheer effrontery in even hinting that anything better than C is possible.maybe for your own good? certainly anything is not impossible, we just open to that, but we dont know how.. since you think you can do it, just prove it in short possible time, not suggesting far more worse example from your wonderland delusion.
remember. when you are done, you are going to do a great length documenting it to make people understand. and thats where peer reviews/discussion will actually take place... and know how many takers and nayers you are going to get.. that number will means something... at this stage.. we just a bunch of blocking stone to you (we tried not to be though), some are just drawing their honest opinions, even if you dont like it.
My background was programming in multiple languages cobol fortran c basic various job control languages and a pile of assemblers ibm 360, icl 1900, pdp 8, pdp 11, 8086, 6502, z80, pic 8 bit, avr 8 bit and a few more that I've forgotten. Language is pretty much irrelevant, like human languages. Can express a concept in any of them. For fun I've written programs using subleq, just to satisfy this thought. It doesn't take a lot to come up with a set of macros that look like instructions in a higher language, then use them.
So I am much more interested in what the proposed language can do rather than how it is expressed.
...
Let's move on from this syntax discussion.
A few years ago I wrote a top octave generator using an Arduino nano (16MHz clock, ATMega328 CPU). It simultaneously produced 12 square waves on 12 pins with frequencies from 4435Hz to 8368Hz with transitions to the nearest 500ns. It would be interesting to see what processor could achieve that with an interpreter. Or even with a compiler. The object code was huge but in the main generated by assembler macros
Or tell the compiler #operator async disable. Since the legacy code didn't use async as a reserved word, this works.
My background was programming in multiple languages cobol fortran c basic various job control languages and a pile of assemblers ibm 360, icl 1900, pdp 8, pdp 11, 8086, 6502, z80, pic 8 bit, avr 8 bit and a few more that I've forgotten. Language is pretty much irrelevant, like human languages. Can express a concept in any of them. For fun I've written programs using subleq, just to satisfy this thought. It doesn't take a lot to come up with a set of macros that look like instructions in a higher language, then use them.
So I am much more interested in what the proposed language can do rather than how it is expressed. Good documentation offsets most of the obfuscation present in any language, and more importantly, what the programmer was trying to achieve (which often is not obvious from the code regardless of how clear the language). Let's move on from this syntax discussion. Let the OP have a language with no reserved words, we can live with that anomaly even if we don't want it. Finish the entree, move on to the main meal.
Mr Ritchie probably has figured it out decades ago, so by realizing this... there is escape plan to wash C's hands off... assembly-program-in-c (https://www.javatpoint.com/assembly-program-in-c) so its impossible in C, only whenever the assembly/machine code make it impossible (unavailable) including THAT arithmetic/logical shift... this is also probably why the C got the bashing of having so many different standards across platforms... want to invent a unified language for all at the very low level? good luck!Most CPU architectures include instructions for arithmetic shift, logical shift and rotates, yet C that "designed for hardware" language doesn't support most of these variants, don't you get it? it was never designed for the applications you keep claiming it was designed for, it wasn't even designed if I can be blunt.As already touched upon, by some in this thread. You may have to be careful, if you support, too low a programming level, in a programming language. It could end up being counter-productive.
Most CPU architectures include instructions for arithmetic shift, logical shift and rotates, yet C that "designed for hardware" language doesn't support most of these variants, don't you get it? it was never designed for the applications you keep claiming it was designed for, it wasn't even designed if I can be blunt.
As already touched upon, by some in this thread. You may have to be careful, if you support, too low a programming level, in a programming language. It could end up being counter-productive.
Let's say, a very low level instruction set, is included as part of the high level language, but it is TOO low a level.
So it might include (just an example, other instructions could have similar, unintended consequences, this instruction has already been mentioned in this thread).
A subtract (assembly like) instruction as part of the high level language, complete with a functionally defined borrow flag, over-flow detection and maybe some other bits and pieces, such as a zero (all zeros) flag.
One or more actual CPUs the new programming language targets, may have this very instruction, so that is fine.
But any other supported architectures (CPUs), may have significantly different subtract instructions. They might use a carry, rather than a borrow flag, and its definitions could vary. There might not be an overflow flag, or the way it works could vary. The zero flag, may be set under different conditions, e.g. index registers may or may not, affect the zero flag, etc.
So, in order to implement the new programming language, and have consistent results, between differing architectures. What would have been a single subtract (machine code) instruction, in one of the supported architectures. Has to be replaced, by lots of instructions, just to emulate the functionality, of a different architecture.
So, for the original/primary supported archetecture, the following program, might be replaced with a single (fast) instruction.
a = a - 2;
Becomes, after compiling:
Subtract-with-borrow 2
But on other archetectures, with completely differing subtract machine code instructions and flag setups. It might look like this:
a = a - 2;
becomes, after compiling:
clear-carry-flag
Subtract-with-carry 2;
Instructions, including branches to invert the carry flag
Instructions to calculate the overflow status
Other instructions to emulate the original subtract instruction, shown above, in the first example
Etc.
So instead of being a fast, single instruction (such as SUBB), it becomes a horrible mess of 10 or 20 instructions to emulate a differing architecture.
So, the C language (I suspect, but I'm NOT sure of the actual reasons), by NOT going too low a level on purpose, avoids having to do lots of messing around and slowing things, because of all the extra instructions needed to fix the compatibility issues, between architectures.
As long as some method of putting in custom assembly code and/or intrinsics, is included in a new language. It should not be necessary, to go too overboard, with low level functionality. Because the programmers, can just put in assembly code or similar, as and where necessary.
I.e. C seems to stick to things, which is relatively common, on just about all architectures, rather than risk taking too many assumptions and/or having to do (potentially slow) fixes, to recreate the compatibility.
A few years ago I wrote a top octave generator using an Arduino nano (16MHz clock, ATMega328 CPU). It simultaneously produced 12 square waves on 12 pins with frequencies from 4435Hz to 8368Hz with transitions to the nearest 500ns. It would be interesting to see what processor could achieve that with an interpreter. Or even with a compiler. The object code was huge but in the main generated by assembler macros
So, you wrote it in assembly :o
Some mpus have hw helpers.
Special programmable circuits that only need glue code.
my-c does not optimize the generated assembly code
It's a problem if you need performance.
But it's not its purpose.
C does support embedded assembly language though, can one go any lower?what kind of nonsense question is that? the plain answer for beginner is no. if yes, please elaborate. you may prove i'm a beginner as well! ;) or maybe we are using different dictionary here? if you keep posting nonsensical thing like this and before, dont be surprised this thread will become amusement center, which from experience, will eventually get locked down, you dont want it to happen do you?
What is "my-c"? as written it sounds like "mice" or maybe "Mike".
I didn't explain the a = a - 2 section very well....you keep rephrasing that same basic stuffs, even if you explained it in pseudo/general idea earlier, anybody with basic/intermediate knowledge should understand very well the point you want to make.. you forget he's an expert an experienced programmer, he already know that yet insist on continuing... let him do his stuffs. you cant win!
But the issue would be that if you included that statement in the language, and people start using it, even when it was not really needed. Any architectures, that DON'T have a 64 bit, bit count instruction, may run that instruction very inefficiently.
Although you are right, that some programmers, might have understood it. It should have been written clearly enough so that all or nearly all, programmers, could understand it.you cant win against nitpickers, and esp trolls (when they became apparent... since no better argument can be given)... requoted below..
C does support embedded assembly language though, can one go any lower?
You raise some good points. But your example using a = a - 2; is something done routinely, even in C. You'd need to show a better example of an abstract idea that's "low level" and that C doesn't support, that you consider would be too low level.
I didn't explain the a = a - 2 section very well.
Another (possibly better) example, would be a low level instruction, which counts how many bits in total, are set (1), in an unsigned, 64 bit integer. Which is useful for some types of algorithms and program types.
So the high level language, could include a low level statement, which counts up the bits set count, works in only 1 CPU clock cycle (because the initial CPU architecture has such an assembly language instruction), for the current CPU architecture, and so runs very quickly.
But the issue would be that if you included that statement in the language, and people start using it, even when it was not really needed. Any architectures, that DON'T have a 64 bit, bit count instruction, may run that instruction very inefficiently.
There are faster methods, but essentially the function would be performing something like:
// Code vaguely based on Pascal
// Count the total bits set in variable x
bit_count = 0;
for bit = 0 to 63 do
begin
if x and 1 == 1 then bit_count++;
x /= 2;
end
There are faster algorithms.C does support embedded assembly language though, can one get any lower level than that?
That is an interesting question. Computers are getting rather complicated these days, because as well as the CPU which processes conventional instructions. There can be various other sections (System on a chip, SoC), such as AI/Machine learning accelerators (technical names can vary), hardware encryption, video encoders/decoders. Even finite state machines (FSMs), FPGAs, the list these days, seems to go on and on.
So I suppose it depends on ones definition. But anyway, technically speaking some of these SoC (names can vary), features, can be at a relatively simple/low level (lower than standard double floating point or 64 bit integers), but typically, it is done at incredibly high speeds. Compared to the main/normal/standard CPU instruction set.
If you run code that uses these intrinsics on hardware that doesn't support the popcnt instruction, the results are unpredictable.
dcl Substr builtin;
dcl Sqrt builtin;
dcl Offset builtin;
dcl Popcount intrinsic;
dcl Emit intrinsic;
dcl Prefetch intrinsic;
Long thread, so I have not read the entire thing. I will say that anytime you are working close to the metal, or even just controlling things, Forth is an excellent choice. Yet, I haven't seen it mentioned very much here.
Forth is my go to language for any programming I do outside of a spreadsheet.
Why are people not more interested in Forth? Is it just a matter of it being so different from languages like C?
C does support embedded assembly language though, can one get any lower level than that?
Long thread, so I have not read the entire thing. I will say that anytime you are working close to the metal, or even just controlling things, Forth is an excellent choice. Yet, I haven't seen it mentioned very much here.
Forth is my go to language for any programming I do outside of a spreadsheet.
Why are people not more interested in Forth? Is it just a matter of it being so different from languages like C?
I'm interested in many languages, I admit to knowing very little about Forth, I read a lot about it years ago, it was used early on when MCU's came out in the 1970s.
So tell me about Forth, what do you like about it?
Long thread, so I have not read the entire thing. I will say that anytime you are working close to the metal, or even just controlling things, Forth is an excellent choice. Yet, I haven't seen it mentioned very much here.
Forth is my go to language for any programming I do outside of a spreadsheet.
Why are people not more interested in Forth? Is it just a matter of it being so different from languages like C?
Why are people not more interested in Forth? Is it just a matter of it being so different from languages like C?
Forth has weird conventions and mechanisms.
I prefer PostScript. It is much improved over Forth, clearer to read, better data structures, and more dynamic by default, though you can "bind" things for speed when needed.
Forth has weird conventions and mechanisms.
I prefer PostScript. It is much improved over Forth, clearer to read, better data structures, and more dynamic by default, though you can "bind" things for speed when needed.
sectorforth, see here:
https://github.com/cesarblum/sectorforth
Fits the entire Forth system into a tiny, single 512 byte sector (of a floppy disk, I think). Which shows the power of both assembly language it is mainly written in, Forth, and some peoples determination, to do things, just because they can.
That is so small, you could fit it into just 8 of the 16 or 32 registers of an AVX-512 equipped processor. Call it just 25% of the AVX-512 accumulators/registers, or an even lower percentage, if you include the mask registers.
How long would it take, to create a Postscript system, complete with all its 400+ commands/instructions (an average of a byte or so, for each one), to fit into a single 512 byte sector?
I think for some new programming languages. Forth's potential, high efficiency in some areas, and the fact that much of it, is actually written in Forth itself, makes it interesting. Because it allows final end users (programmers), to freely extend its functionality, as if it was part of the original interpreter.
But as suggested in quote, PostScript, seems interesting to consider as well. Thanks for mentioning it. I seem to very strongly associate it with Laser Printers, rather than seriously considering it as a programming language, for various reasons. Also, I consider GhostScript, a rather unreliable and buggy way of trying the language out. At least it (GhostScript) was (rather/very buggy), a long time ago.
C does support embedded assembly language though, can one get any lower level than that?One of the features of softcore FPGA CPUs is that you can implement specialised operations using bit patterns which were previously undefined in the instruction set. These bit patterns can be emitted directly by the compiler without using an assembler.
These bit patterns can be emitted directly by the compiler without using an assembler.
512 bytes is very small, even for a Forth. Are there no additional word definitions that get loaded as source? Source compilation is so fast, that systems often have just a core in binary, and load the remainder from source.
I've wanted to try it out for some time, but been put off, because you have to sort out some kind of 386 system (e.g. an emulator), to try it out.
Looking at the hello world, example program. Here:
https://github.com/cesarblum/sectorforth/blob/master/examples/01-helloworld.f
It seems there are only 8 primitive words (see readme file for more information, sorry, I called them functions in my earlier post, I tend to call things functions or procedures, or even subroutines, even if the language prefers other names). So the hello world example program, defines a very large number of relatively primitivefunctionswords, in order to make the required program.
In fairness, 512 bytes is rather tight to fit a full (ALL) Forth word, system into. But even so, to have enough of a Forth System, to make a usable entity, is still very impressive!
E.g. The hello world program has definitions, such as:Quote\ stack manipulation words
: drop ( x y -- x ) dup - + ;
: over ( x y -- x y x ) sp@ 2 + @ ;
: swap ( x y -- y x ) over over sp@ 6 + ! sp@ 2 + ! ;
: nip ( x y -- y ) swap drop ;
: 2dup ( x y -- x y x y ) over over ;
: 2drop ( x y -- ) drop drop ;
So it is sort of cheating. But it also shows the expressive power of Forth, rather nicely.
QuoteThese bit patterns can be emitted directly by the compiler without using an assembler.
emitted how? By inline asm DC?
If the machine layer is not awere of them and they are emmited as "raw" data, then it's a trick
Sure it works, but it's an hack
Does not pass QA
Makes ICE confused
Makes the source dirty
Makes the compiler unable to understand what's going on
Makes the source not portable
Opens doors to undefined run time behaviors
How long would it take, to create a Postscript system, complete with all its 400+ commands/instructions (an average of a byte or so, for each one), to fit into a single 512 byte sector?
Also, I consider GhostScript, a rather unreliable and buggy way of trying the language out. At least it (GhostScript) was (rather/very buggy), a long time ago.
I don't know of any PostScript tools that are generally available for embedded systems. Would using PS on an ARM with 16 kB of Flash even be possible?
I don't know of any PostScript tools that are generally available for embedded systems. Would using PS on an ARM with 16 kB of Flash even be possible?
Of course. Why not? It's very similar in complexity to Forth, and much simpler than, say, Python or JavaScript, both of which have small microcontroller implementations.
PostScript works best in a non-interactive batch processing mode.
MCLK_Enable <= ( MCLK_Divide = 0 );
BCLK_Enable <= ( BCLK_Divide = 0 );
LRCK_Enable <= ( LRCK_Divide = 0 );
: test ( MCLK_Divide, BCLK_Divide, LRCK_Divide -- MCLK_Enable, BCLK_Enable, LRCK_Enable)
0= rot 0= rot 0= rot ;
MCLK_Divide 0= rot DoSomethingMCLKWithTheResult
BCLK_Divide 0= rot DoSomethingBCLKWithTheResult
LRCK_Divide 0= rot DoSomethingLRCKWithTheResult
ICE??? If you need an ICE, your program is far too complex.
You use a lot of nasty sounding adjectives. Where have you been working, porn web sites?
Calling it a trick or a hack is silly. I designed a bit slice processor as a DMA controller for an array processor (the supercomputer of the 70s). I wanted to use some instructions that I created, which were not part of the assembler by default. I added them. QA didn't complain. I just had to document it.
QA didn't complain. I just had to document it.
QuotePostScript works best in a non-interactive batch processing mode.
This is the exact opposite of Forth... well, I shouldn't say opposite. Forth handles batch mode just fine. But the interactivity of Forth is a powerful debugging technique.
The first word Linux driver written in Rust for apple m1 is interesting
QuoteCalling it a trick or a hack is silly. I designed a bit slice processor as a DMA controller for an array processor (the supercomputer of the 70s). I wanted to use some instructions that I created, which were not part of the assembler by default. I added them. QA didn't complain. I just had to document it.
Whatever, emitted how, so?
If it's inline asm DC. Then it's an hack.
QuoteQA didn't complain. I just had to document it.
Doc is good but doesn't pay money.
Code rejected means no money.
QuotePostScript works best in a non-interactive batch processing mode.
This is the exact opposite of Forth... well, I shouldn't say opposite. Forth handles batch mode just fine. But the interactivity of Forth is a powerful debugging technique.
EVERY programming language works better in non-interactive mode for large and complex programs.
It's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
This means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
This is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
The first word Linux driver written in Rust for apple m1 is interesting
Have you looked at it?
EVERY programming language works better in non-interactive mode for large and complex programs.
That's very bogus. Any large program is developed in pieces, which are defined in pieces, etc., until they constitute small programs.
When people run an ICE on a project, they are using the same methods used interactively, but with much more complexity because of the massive code base.
Forth is actually a bit famous for simplifying most coding. Once you learn the philosophy, it becomes second nature to work in the "Forth" way testing the lower level code thoroughly before integrating at the next level.
But people who are entrenched with using tools like ICE, don't appreciate, or possibly understand simpler ways of working.
QuoteIt's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
I was responding to this quote from the web page...
"PostScript works best in a non-interactive batch processing mode."
Now you are saying Postscript is interactive. Someone needs to make up their minds.
QuoteThis means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
I'm no expert on compilation, but it sounds to me that late-binding would be a bit slow.
QuoteThis is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
Actually, Forth supports something similar, using words DEFER and IS. This must be set up at compile time, but can be changed at run time. So interactive debugging with alternate definitions for a given word is supported. There is no run time penalty on most systems, while on others it would be slight. It is essentially like changing the subroutine call by using an intermediary.
No. All of the code generated by our Oberon compilers (as is true for most, if not all, of Prof Wirth's compilers based on) is written as 16-bit or 32-bit words directly into the binary file. No assembly code is generated; no assembler pass is necessary or is used.QuoteThese bit patterns can be emitted directly by the compiler without using an assembler.emitted how? By inline asm DC?
Instructions are emitted sequentially and emitted by the four procedures Put0, Put1, Put2, Put3. They directly correspond to the instruction formats of the RISC processor (see Chapter 11). The instructions are stored in the array code and the compiler variable pc serves as running index.
EVERY programming language works better in non-interactive mode for large and complex programs.
That's very bogus. Any large program is developed in pieces, which are defined in pieces, etc., until they constitute small programs.
When people run an ICE on a project, they are using the same methods used interactively, but with much more complexity because of the massive code base.
Forth is actually a bit famous for simplifying most coding. Once you learn the philosophy, it becomes second nature to work in the "Forth" way testing the lower level code thoroughly before integrating at the next level.
But people who are entrenched with using tools like ICE, don't appreciate, or possibly understand simpler ways of working.
I wouldn't know an ICE if it bit me in the arse.
BASIC is interactive. Python is interactive. Both are better written in a text editor, except in rare circumstances where you don't know WTF the the language does or want to poke at some peripheral.
QuoteQuoteIt's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
I was responding to this quote from the web page...
"PostScript works best in a non-interactive batch processing mode."
Now you are saying Postscript is interactive. Someone needs to make up their minds.
Dude, that's not my web page. It's the opinion of someone who is not in this discussion.
Something being interactive doesn't mean you want to work that way all the time.
QuoteQuoteThis means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
I'm no expert on compilation, but it sounds to me that late-binding would be a bit slow.
OF COURSE it is. It's also far more flexible. That's why lots and lots of people program most of their application logic in slow-as-molasses Python, and do the few bits where you can tell the difference in C.
It's better if you can do it all in one language.QuoteQuoteThis is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
Actually, Forth supports something similar, using words DEFER and IS. This must be set up at compile time, but can be changed at run time. So interactive debugging with alternate definitions for a given word is supported. There is no run time penalty on most systems, while on others it would be slight. It is essentially like changing the subroutine call by using an intermediary.
Which means Forth has is bass-ackwards, as the flexible slow method should be the DEFAULT. not something you have to jump through hoops to get.
I've been using Forth, on and off for more than 40 years, when appropriate. And PostScript for 37 years.
typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Here are my 2c (Did not read through the whole thread, so please forgive me if these are dupilcates):
1. In C the arrays are quite problematic when passed to functions, because the length of the array is not implicitly passed as an argument. So, the new language should pass the length of an array implicitly to the function to be called. The length of the passed array needs to be accessible inside the called function. Enabling/disabling run-time checks for validating array accesses should be supported globally / by compilation unit / by function, and during unit testing. Also, when passing a variable or an array as a void*, the caller should pass the total size of the item in bytes to the called function.
2. In C handling of endianess when declaring structs and bit fields required some work in order to make code portable across different architectures: Keywords and language constructs for native / big endian / little endian should be provided, and the compiler should take care of producing code for the correct endianess.
3. In C the padding of structs requires careful design to get it right, especially when porting to different architectures. The compiler should provide simple and intuitive means for specifying and asserting the padding.
4. Design by contract (https://en.wikipedia.org/wiki/Design_by_contract) should be supported natively by the compiler so that the verification of the contracts can be enabled during compilation time and when running the unit tests. The run-time validation of the contract asserts, pre- and postcondition contracts should be controllable globally / by compilation unit / by function when creating for the actual production build. In C this can be done using macros, but this should be built-in into the language, and should be supported during run-time system.
5. Modern C compilers complain when comparing unsigned value to a signed value, which is good. But modern C compilers allow assigning unsigned variables to signed variables, and vice versa. Assigning unsigned variables to signed variables, and vice versa, should be flagged as an error by default in general. Literal values should be casted automatically by the compiler, of course. If the user wants to assign signed variables to unsigned variables, and vice versa, the operation should be done explicitly by the programmer.
6. Native 8-bit unsigned and signed data types, and a separate 8-bit char data type which is not compatible with the integer types without proper casting.
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Edit:
8. More intuitive way to work with bits/bitmasks.
I don't recall, that was nearly 50 years ago. I seem to recall one supervisor being initially upset that technically, this used a different assembler, so it must not have been a DC, rather a "hack" to the assembler code.
But once it was thoroughly documented in the project, he was happy.
You should never learn Forth coding. I am confident it would blow your mind... and not in the good way. Some people aren't bound so rigidly.
Sorry, you are incomprehensible. Can you speak English?
if (expr)
stmts;
elif (expr)
stmts;
else
stmts;
endif
if (expr)
stmts;
endif
if (expr)
stmts;
else
stmts;
endif
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Would you also object to apples = apples + 1; ?
If not, why not? It's a new type (casting away the fact that typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3).
Shouldn't it be apples = (apple_t)((int8_t)apples + 1);?
After all, apple_t and int8_t are no longer the same type (if they were, so would be orange_t and we are back to C semantics), so why should I expect to have the same operations defined on them.
apples = apples + 1;
apples++;
apples = (apples << 1) | 1;
apples = 1 + oranges;
apples = (oranges << 1) | 1;
apples = apples + oranges * 2
BTW: what would be the if-then-else if-else problem?
typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3)I would like to see that typedef defines a new type. For declaring a synonym, a new keyword
4. Design by contract
Here are my 2c (Did not read through the whole thread, so please forgive me if these are dupilcates):
1. In C the arrays are quite problematic when passed to functions, because the length of the array is not implicitly passed as an argument. So, the new language should pass the length of an array implicitly to the function to be called. The length of the passed array needs to be accessible inside the called function. Enabling/disabling run-time checks for validating array accesses should be supported globally / by compilation unit / by function, and during unit testing. Also, when passing a variable or an array as a void*, the caller should pass the total size of the item in bytes to the called function.
dcl matrix(10,20) string(64) varying;
func scan_for_text(table) returns bool
arg table(*,*) string(64);
var x = Dim(table,0); // i.e 10
var y = Dim(table,1); // i.e 20
var isup = is_uppercase(table(3,7));
end
func is_uppercase(text) returns bool
arg text string(*);
end
2. In C handling of endianess when declaring structs and bit fields required some work in order to make code portable across different architectures: Keywords and language constructs for native / big endian / little endian should be provided, and the compiler should take care of producing code for the correct endianess.
3. In C the padding of structs requires careful design to get it right, especially when porting to different architectures. The compiler should provide simple and intuitive means for specifying and asserting the padding.
4. Design by contract (https://en.wikipedia.org/wiki/Design_by_contract) should be supported natively by the compiler so that the verification of the contracts can be enabled during compilation time and when running the unit tests. The run-time validation of the contract asserts, pre- and postcondition contracts should be controllable globally / by compilation unit / by function when creating for the actual production build. In C this can be done using macros, but this should be built-in into the language, and should be supported during run-time system.
5. Modern C compilers complain when comparing unsigned value to a signed value, which is good. But modern C compilers allow assigning unsigned variables to signed variables, and vice versa. Assigning unsigned variables to signed variables, and vice versa, should be flagged as an error by default in general. Literal values should be casted automatically by the compiler, of course. If the user wants to assign signed variables to unsigned variables, and vice versa, the operation should be done explicitly by the programmer.
6. Native 8-bit unsigned and signed data types, and a separate 8-bit char data type which is not compatible with the integer types without proper casting.
dcl counter bin(8) signed;
dcl totals bin(16) unsigned;
dcl rate bin(16,4) signed;
dcl interim bin(12); // defaults to signed (perhaps)
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
8. More intuitive way to work with bits/bitmasks.
I know that Arm supports both types, how would you envisage this looking to a developer? what aspects of code are influenced by this?
I know that Arm supports both types, how would you envisage this looking to a developer? what aspects of code are influenced by this?
Endianness
At early boot, SONY PSX1 (MIPS R3000), as well as POWER10, can be configured LE or BE with one bit.
Once configured, it is active and cannot be easily changed until the hardware is reset.
Sony configured the PSX1 CPU as LE. Years ago I hacked the firmware with a ROMemulator to force it as BE. Kind of toy-hack, funny to do, nothing serious.
Taylos sells their POWER10 configured as LE, so I reflashed my boss's POWER10 workstation firmware to be PPC64/BE compliant. This saved me houndred hours of work, because I only have PPC64/BE stages available and prepairing a PPC64/LE .... costs no less than two weeks.
Sure, with POWER10, MIPS5+ and some modern ARMs there are also instructions that convert the data { 16, 32 } bits LE <----> BE.
Mac_part, which is BE because designed for PowerMac, is one of the recent things I fixed on ARM64, which is LE and can't be forced BE without pain.
So, I wrote a C function to convert endianness. Easy job, you can even "inline" it. When you mind its needs, well, you find it only requires endianness conversion when mounting a partition, and it's fine that way until unmount.
Coprocessors and high-internet-working like Tulip chip can offload CPU but when they shoot ... they wants BE-only (and your target is LE) large-block data ready, so it can be worth the effort of an assembly module that directly use hardware instructions.
It depends on how often you need it.
I mean, nice to have feature, but it's not a primary need :-//
I don't recall, that was nearly 50 years ago. I seem to recall one supervisor being initially upset that technically, this used a different assembler, so it must not have been a DC, rather a "hack" to the assembler code.
No way, if it was a C file with assembly inline DC, it was an hack!
my-c can fix this, but it requires an advanced set of internal mechanisms to define new operators that map to new hw opcodes in a consistent way to let the compiler know their boundaries, their negative side effect (if any), provide ways to observe and verify (this is useful for ICE test cases) and also provide code rejection on error.
[] -> { ok, maybe, nothing }But once it was thoroughly documented in the project, he was happy.
documenting hacks, well ... yes, bettern than nothingYou should never learn Forth coding. I am confident it would blow your mind... and not in the good way. Some people aren't bound so rigidly.
I have ZERO interest in Forth. It's not even close to what I think.
I make programs in C and Ada for a living.
Recently I am looking at RUST with interest.
Sorry, you are incomprehensible. Can you speak English?
You said that QA doesn't complain (when) you just have to document your code.
I said that doc is always a good practice but doesn't pay money.
to pay : give someone something in return for { work done, goods received, debt incurred, favor done }
pay(someone, something) ->
{
work done: your clients(code commited, QA passed) -> return you money
favor done: your colleagues(code documented, hacks demystified) -> return you gratitude
}
Documenting things is always a great thing, it helps you with future work on the same code, it repays your colleagues gratitude in return (if they have to work on your code), but what only matters to you at the end of the week is whether the code passes or fails QA.
My customers won't pay a cent for the document, and won't pay a cent if the code fails QA.
Thus, documenting has a lower priority.
It's not good, but it's how it works.
So what are the code constructs that are not symmetrical with respect to endianness? For example a = b + c ; will (semantically) behave the same way yes? what code does not or would not behave symmetrically?
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Such a strict type checking might be good, or not. Opinions differ.
Not even Pascal does what you are suggesting.
Would you also object to apples = apples + 1; ?
If not, why not? It's a new type (casting away the fact that typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3).
Shouldn't it be apples = (apple_t)((int8_t)apples + 1);?
After all, apple_t and int8_t are no longer the same type (if they were, so would be orange_t and we are back to C semantics), so why should I expect to have the same operations defined on them.
BTW: what would be the if-then-else if-else problem?
I don't need you to be a dictionary. I just need you to use the English language well.
Porn website.
Spoken like a hack.
Where you live perhaps.
Last thing for now: Checked assignments, which will be checked at run-time so that the compiler will generate code to check that the assigned value is within the allowed numerical range of the variable where the results will be assigned to. Trying to assign a value outside the allowed range will generate a run-time error. This feature should be enabled during unit testing, and should be controllable (enable/disable) globally / by compilation unit / by function during build time.
Just some random thoughts: Probably a new operator := could be defined for checked assignment. Probably a checked block could be defined so that all assignments inside the checked block will be checked during run-time if the compile-time option for the checked assignment was enabled globally or by compilation unit / by function. **. I dunno.
Edit:
** Maybe some kind of saturated assignment could be defined so that if the value to be assigned is outside the valid numerical range, the programmer can provide the upper and lower limits of a value to be assigned. If the programmer doesn't provide the limits, the compiler will use the minimum and maximum values automagically? Just thinking aloud here.
dcl table(10,10) bin(16);
table(4,11) = 100; // passes shallow check because it access absolute element 51 out of 100, a valid array element.
So what are the code constructs that are not symmetrical with respect to endianness? For example a = b + c ; will (semantically) behave the same way yes? what code does not or would not behave symmetrically?
Once BE/LE defined at boot (x86 is LE-only, it's hw wired that way), a CPU has an endianness, and so it operates untill reset.
Problems are with the load/store only when it has to access devices that has/need different endianness.
Here you need endianess conversion.
The simplest example is mac_part.
Fields are BE when you read them from the partition on the disk.
You read BE, you convert LE, so your LE-CPU (x86) can understand fields.
You keep them LE in your CPU memory space untill you need to modify something and update its copy on disk. So, you convert LE back to BE, and force sync.
With the Co-Processor, you prepare a temporary buffer, LE to BE, to feed its needs.
And you consume its block-response, BE to LE, to serve your kernel driver needs.
dcl IsBigEndian builtin;
dcl LittleEndian builtin;
// assume this data item has been populated by some IO read, and some_ptr has therefore been set
dcl partition_data bin(32) signed based(some_ptr);
if IsBigEndian() then // Both builtin functions are included for convenience, so we also have IsLittleEndian
partition_data = LittleEndian (partition_data);
end
data_header = LittleEndian (data_header);
It could be extended too so that we can do this to a structure, where all endian dependent members are automatically taken care of:The version working on a single, not structured, variable is best suited to a default library, it should not be part of the language, same goes with the endianness check.Code: [Select]data_header = LittleEndian (data_header);
Where data_header is an aggregate like an array/structure or both, this would impact numeric fields, floats and pointers/offsets even...
One final note: Classical if - then - elseif - else probelm. My suggestion is something like this:Code: [Select]if (expr)
stmts;
elif (expr)
stmts;
else
stmts;
endif
Thus a simple test:Code: [Select]if (expr)
stmts;
endif
A simple test with else clause:Code: [Select]if (expr)
stmts;
else
stmts;
endif
etc.
if count > 100 then
call do_something();
else
if count < 0 then
call do_something_else();
else
if count > max then
call where_done();
end;
end;
end;
One final note: Classical if - then - elseif - else probelm. My suggestion is something like this:Code: [Select]if (expr)
stmts;
elif (expr)
stmts;
else
stmts;
endif
Thus a simple test:Code: [Select]if (expr)
stmts;
endif
A simple test with else clause:Code: [Select]if (expr)
stmts;
else
stmts;
endif
etc.
This is certainly an interesting grammar design question. First though when you say "problem" what do you mean? Do you mean it presents a parsing challenge or a cognitive challenge to a human reader?
if (expr)
stmt;
stmt;
if (expr)
stmt;
stmt;
endif
while (expr)
stmts;
endwhile
for (for-loop-control-block)
stmts;
endfor
While at talking about the C-language, grammar and braces, it would be nice if the language would be cleaned up so that the braces could be eliminated as much as possible.
One final note: Classical if - then - elseif - else probelm. My suggestion is something like this:Code: [Select]if (expr)
stmts;
elif (expr)
stmts;
else
stmts;
endif
Thus a simple test:Code: [Select]if (expr)
stmts;
endif
A simple test with else clause:Code: [Select]if (expr)
stmts;
else
stmts;
endif
etc.
This is certainly an interesting grammar design question. First though when you say "problem" what do you mean? Do you mean it presents a parsing challenge or a cognitive challenge to a human reader?
C and C++, as well as some Algol-languages, may suffer more or less from so called dangling else syndrome.
https://en.wikipedia.org/wiki/Dangling_else
Adding this endif keyword will fix potential grammar ambiguity, help with parsing, and also help the human reader.
This is a simple error to make in C-family languages, but sometimes hard to spot:Code: [Select]if (expr)
stmt;
stmt;
When the if-statement block will always end with endif keyword, this problem will be fixed. And no braces are required, either.Code: [Select]if (expr)
stmt;
stmt;
endif
I would fix the while-statement and for-statement, as well.Code: [Select]while (expr)
stmts;
endwhileCode: [Select]for (for-loop-control-block)
stmts;
endfor
Whether to use endwhile and endfor, or just plain end, I do not have any strong opinion. The idea is just to avoid those easy-to-make human errors and help the parser with detecting errors.
While at talking about the C-language, grammar and braces, it would be nice if the language would be cleaned up so that the braces could be eliminated as much as possible. The function-blocks, struct and enums etc. could be defined so that no braces are required, and the end of the blocks would contain just an end keyword instead.
if expression then
do;
stmt;
stmt;
end;
else
do;
stmt;
stmt;
end;
While at talking about the C-language, grammar and braces, it would be nice if the language would be cleaned up so that the braces could be eliminated as much as possible.
Why would you want to eliminate braces as much as possible? One can disagree about the exact symbol ({ } instead of something else like ( ) or begin end), but braces in syntactically C-like language do excellent job by visually and logically grouping things together - statements in code, variables in a struct, values in compound initializer, and so on. Prefer explicit over implicit; explicit grouping, in this case. Typing the braces is little work, and can be further automated in an IDE.
As a beginner, I wanted to "eliminate braces" to make code look smaller, but nowadays I find myself doing the opposite; one can group related statements in a { } block even if that isn't strictly necessary, creating a local "namespace", and making later refactoring into a function more obvious.
There are more important things in engineering than deciding where to put braces. It doesn't fricking matter as long as readability is preserved.
But anyway, again most of what we can read in this thread is about grammar and syntax, and very little if anything about fundamental concepts of programming.
The contract thing is one of the rare exceptions here. But it's nothing new either, it's in Ada 2012, has been in Spark for longer than this, and in Eiffel longer ago yet, and a (albeit small) number of other languages. This is definitely an interesting and important feature and definitely not trivial to implement properly.
Well, not using braces sure avoids fights about how to place them in source code. :D
# my-c --fo file.myc
won't compile the source, instead it will parse file.myc and output it well formatted.I don't need you to be a dictionary. I just need you to use the English language well.
Porn website.
Spoken like a hack.
Where you live perhaps.
Alright, enjoy my ignore list.
I'm not proposing a language, go on, check all the posts for yourself. You can't talk about language as distinct from "how it is expressed", that IS LANGUAGE. We can't even discuss languages without using a language, anyway, I'm tired, good night.Of course language and what is being expressed are separate:
One final note: Classical if - then - elseif - else probelm. My suggestion is something like this:Code: [Select]if (expr)
stmts;
elif (expr)
stmts;
else
stmts;
endif
Thus a simple test:Code: [Select]if (expr)
stmts;
endif
A simple test with else clause:Code: [Select]if (expr)
stmts;
else
stmts;
endif
etc.
if count > 100 then
call do_something();
elif count < 0 then
call do_something_else();
elif count > max then
call where_done();
else
return;
end;
if count > 100 then
call do_something();
elif count < 0 then
call do_something_else();
call do_whatever();
elif count > max then
call where_done();
if we_are_still_here then
return;
elif we_did_not_crash then
call crash();
else
call stop();
else
return;
end;
if count > 100 then
call do_something();
elif count < 0 then
call do_something_else();
call do_whatever();
elif count > max then
call where_done();
if we_are_still_here then
return;
elif we_did_not_crash then
call crash();
else
call stop();
END;
else
return;
end;
About code generation and compiler optimization: There are already quite many open source compilers which provide optimized code generation, along with a full tool-chain, thus inventing a wheel may not be necessary. If the parser for a new language syntax could produce an AST which is compatible with the existing compiler technology, the customized compiler front end could be used with the existing compilers which would simplify the whole process.
The GNU compiler suite is using this approach, as there is a language-specific front-end coupled with a generic/universal back-end which is not targeted to any particular language or architecture what so ever. Unfortunately I do not know the internals of the GNU compiler suite, so I cannot provide any detailed information how hard this approach would be. However, since the source code for the GNU compiler suite is freely available, it might be possible to figure out what is takes to create a new front end for a new language. Of course any other freely available compiler suite would be a good candidate, too.
Before writing a full-blown compiler, one could create a source code translator for the new language producing valid C language (for example). This would simplify the process of trying out and prototyping different constructs in the new language.
About code generation and compiler optimization: There are already quite many open source compilers which provide optimized code generation, along with a full tool-chain, thus inventing a wheel may not be necessary. If the parser for a new language syntax could produce an AST which is compatible with the existing compiler technology, the customized compiler front end could be used with the existing compilers which would simplify the whole process.
The GNU compiler suite is using this approach, as there is a language-specific front-end coupled with a generic/universal back-end which is not targeted to any particular language or architecture what so ever. Unfortunately I do not know the internals of the GNU compiler suite, so I cannot provide any detailed information how hard this approach would be. However, since the source code for the GNU compiler suite is freely available, it might be possible to figure out what is takes to create a new front end for a new language. Of course any other freely available compiler suite would be a good candidate, too.
Before writing a full-blown compiler, one could create a source code translator for the new language producing valid C language (for example). This would simplify the process of trying out and prototyping different constructs in the new language.
Yesss, yesss, translate it to C, "The only true systems languagage" ᵀᴹ , the first C++ "compilers" did the same >:D.
About code generation and compiler optimization: There are already quite many open source compilers which provide optimized code generation, along with a full tool-chain, thus inventing a wheel may not be necessary. If the parser for a new language syntax could produce an AST which is compatible with the existing compiler technology, the customized compiler front end could be used with the existing compilers which would simplify the whole process.
The GNU compiler suite is using this approach, as there is a language-specific front-end coupled with a generic/universal back-end which is not targeted to any particular language or architecture what so ever. Unfortunately I do not know the internals of the GNU compiler suite, so I cannot provide any detailed information how hard this approach would be. However, since the source code for the GNU compiler suite is freely available, it might be possible to figure out what is takes to create a new front end for a new language. Of course any other freely available compiler suite would be a good candidate, too.
Before writing a full-blown compiler, one could create a source code translator for the new language producing valid C language (for example). This would simplify the process of trying out and prototyping different constructs in the new language.
Yesss, yesss, translate it to C, "The only true systems languagage" ᵀᴹ , the first C++ "compilers" did the same >:D.
For example C has no function nesting, representing that - in a general way - in C requires abstracting the concept, this is why a more machine like abstraction might be better, in fact generating assembly language embedded in C might be better then a C dev environment could be used as is.
proc test(x)
value = DEF ABC 2DC0 6BA0:h * 100; // note, no more than a single space can be the separator.
call reboot(FACE DEFB 10C0:H + 239:H);
value = 100 123.45;
value = 1011 FC4.7F:h;
value = 1010 1010;
value = 123:d;
value = 1010 1010:b;
octal = 765:o;
end;
I've managed to get Antlr to recognize the grammar's support for numeric literals in multiple bases and which can contain readability spaces (or underscore) as separators. All of these are recognized:Code: [Select]proc test(x)
value = DEF ABC 2DC0 6BA0:h * 100; // note, no more than a single space can be the separator.
call reboot(FACE DEFB 10C0:H + 239:H);
value = 100 123.45;
value = 1011 FC4.7F:h;
value = 1010 1010;
value = 123:d;
value = 1010 1010:b;
octal = 765:o;
end;
There are four kinds of numeric literals, binary, octal, decimal and hex, they all need a colon-char base designator except dec, for which this can be omitted.
For example C has no function nesting, representing that - in a general way - in C requires abstracting the concept, this is why a more machine like abstraction might be better, in fact generating assembly language embedded in C might be better then a C dev environment could be used as is.
GNU C has an extension for function nesting.
For example C has no function nesting, representing that - in a general way - in C requires abstracting the concept, this is why a more machine like abstraction might be better, in fact generating assembly language embedded in C might be better then a C dev environment could be used as is.
GNU C has an extension for function nesting.
You don't need to generate nested functions in the C output to implement source language nested functions.
For example C has no function nesting, representing that - in a general way - in C requires abstracting the concept, this is why a more machine like abstraction might be better, in fact generating assembly language embedded in C might be better then a C dev environment could be used as is.
GNU C has an extension for function nesting.
You don't need to generate nested functions in the C output to implement source language nested functions.
But it would be much easier if the compiler uses the GNU C extension for the nested functions. The nested functions should be able to access the context of the surrounding function(s). Without nesting, accessing the context of the surrounding function(s) may get really difficult to implement.
For example C has no function nesting, representing that - in a general way - in C requires abstracting the concept, this is why a more machine like abstraction might be better, in fact generating assembly language embedded in C might be better then a C dev environment could be used as is.
GNU C has an extension for function nesting.
You don't need to generate nested functions in the C output to implement source language nested functions.
But it would be much easier if the compiler uses the GNU C extension for the nested functions. The nested functions should be able to access the context of the surrounding function(s). Without nesting, accessing the context of the surrounding function(s) may get really difficult to implement.
Easier, but maybe you don't like the way that compiler implements it, or you want to be able to use other C compilers too (MSVC, SDCC, whatever...)
And it's not hard at all. For example each function declares a struct type with all its local variables in, then declares just one actual local variable of that type. The address of it can be passed as an extra argument to "nested" functions ("static link"), or stored in a global array ("display").
Or only put the variables that are actually used by the nested function in that struct. Or allocate that struct on the heap not the stack so you have a proper closure, not simply nested functions -- that's a common language feature that you CAN'T implement using gcc nested functions.
This is all completely trivial compared to the rest of the work of writing a compiler.
<snip>
memory alignment, field ordering, padding
big/little endian issues
coroutines and cofunctions
exception support
runtime checks
fixed point arithmetic
nested procedures/functions
argument passing
contracts
computed goto
offsets (as opposed to pointers)
array and string metadata
bit data type support
<snip>
I've managed to get Antlr to recognize the grammar's support for numeric literals in multiple bases and which can contain readability spaces (or underscore) as separators. All of these are recognized:Code: [Select]proc test(x)
value = DEF ABC 2DC0 6BA0:h * 100; // note, no more than a single space can be the separator.
call reboot(FACE DEFB 10C0:H + 239:H);
value = 100 123.45;
value = 1011 FC4.7F:h;
value = 1010 1010;
value = 123:d;
value = 1010 1010:b;
octal = 765:o;
end;
There are four kinds of numeric literals, binary, octal, decimal and hex, they all need a colon-char base designator except dec, for which this can be omitted.
Personal opinion: I do not like spaces in the literals. Underscore, for example, would be better.
From the tokenizer/parser point of view things would be much easier if the spaces would not be allowed in literals. See also my comment about the tools below.
Edit: There has also been some discussion on how to group the digits in numerical literals. I guess that it would be possible to build a simple tool using regexpr rule(s) for checking the grouping of the digits for floating point, decimal, hex, octal and binary numbers of person's liking. Just run the checker as part of the build process, and the checker will fail the build process if the numerical literals are using invalid grouping. Building a directive for the rules of digit grouping into the language grammar could be also possible, but I am not sure if that would be a wise choice (without extensive prototyping and evaluation at least).
---
At some point in this thread there was a discussion whether to use reserved words or not. I think the history of the programming languages have shown that using reserved words makes the grammar easier to parse and reduce ambiguity. Just keep the grammar as simple as possible.
About tools: All tools - such as code formatters, IDEs, refactoring tools etc. - would benefit from a simple grammar without ambiguity.
We have something like 70 years of knowledge about programming languages, so there is no need to repeat the problems of the earlier programming languages.
Typename<Type<Other>>
Typename<Type<Other> >
<snip>
memory alignment, field ordering, padding
big/little endian issues
coroutines and cofunctions
exception support
runtime checks
fixed point arithmetic
nested procedures/functions
argument passing
contracts
computed goto
offsets (as opposed to pointers)
array and string metadata
bit data type support
<snip>
in the spirit of C++, any plans for passing an argument of a function as reference, too? That would remove the need of using the dereference-operator when accessing a variable inside the function that was passed as a reference. Passing arguments as a reference will also remove the need for checking NULL pointer values for the arguments.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
call set_timer((timer_id));
<snip>
memory alignment, field ordering, padding
big/little endian issues
coroutines and cofunctions
exception support
runtime checks
fixed point arithmetic
nested procedures/functions
argument passing
contracts
computed goto
offsets (as opposed to pointers)
array and string metadata
bit data type support
<snip>
in the spirit of C++, any plans for passing an argument of a function as reference, too? That would remove the need of using the dereference-operator when accessing a variable inside the function that was passed as a reference. Passing arguments as a reference will also remove the need for checking NULL pointer values for the arguments.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:Code: [Select]
call set_timer((timer_id));
That makes the arg into an expression and expressions are always evaluated into a temporary and a ref to that temp then passed. No need for & and * all over the place!
I want to stress that the grammar has no ambiguities, C++ even C do have grammar ambiguities, you can read about these, C++ also has the most complex grammar of any programming language, littered with edge cases and ifs and buts, until recently this construct was illegal in C++Code: [Select]Typename<Type<Other>>
Instead one was forced to writeCode: [Select]Typename<Type<Other> >
That's frankly ridiculous (the >> is recognized as a shift operator) and can be traced back to sloppy or hasty grammar choices, I talk about this a lot but that's because its very important, without careful design you get all the syntax mess we see in C++, C# and so on.
<snip>
memory alignment, field ordering, padding
big/little endian issues
coroutines and cofunctions
exception support
runtime checks
fixed point arithmetic
nested procedures/functions
argument passing
contracts
computed goto
offsets (as opposed to pointers)
array and string metadata
bit data type support
<snip>
in the spirit of C++, any plans for passing an argument of a function as reference, too? That would remove the need of using the dereference-operator when accessing a variable inside the function that was passed as a reference. Passing arguments as a reference will also remove the need for checking NULL pointer values for the arguments.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:Code: [Select]
call set_timer((timer_id));
That makes the arg into an expression and expressions are always evaluated into a temporary and a ref to that temp then passed. No need for & and * all over the place!
The function declaration should be the place where it is defined whether an argument is passed by value or by reference.
I want to stress that the grammar has no ambiguities, C++ even C do have grammar ambiguities, you can read about these, C++ also has the most complex grammar of any programming language, littered with edge cases and ifs and buts, until recently this construct was illegal in C++Code: [Select]Typename<Type<Other>>
Instead one was forced to writeCode: [Select]Typename<Type<Other> >
That's frankly ridiculous (the >> is recognized as a shift operator) and can be traced back to sloppy or hasty grammar choices, I talk about this a lot but that's because its very important, without careful design you get all the syntax mess we see in C++, C# and so on.
Ada has a major blooper too as they are using parenthesis () for array index instead of brackets []. Now it is impossible to determine whether a(n) is a function call or an array element access, without checking the actual definition.
Crafting a programming language is really hard, and it is very easy to make mistakes that may carry on for decades.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
Or avoid "the billion dollar mistake" altogether.
https://en.wikipedia.org/wiki/Null_pointer#History
select {
case event1
// handle the event
...
break ;
case event2
// handle the event
...
break ;
}
That statement would pause until either of the events occur and then execute the code within the relevant case. Although the select waits on several events, only one of the events would be handled by the statement when an event occurs.Why not provide a construct that directly helps people doing low-level embedded programming?
For example, a select statement that is similar to a C switch statement:Code: [Select]select {
That statement would pause until either of the events occur and then execute the code within the relevant case. Although the select waits on several events, only one of the events would be handled by the statement when an event occurs.
case event1
// handle the event
...
break ;
case event2
// handle the event
...
break ;
}
An event could be any of:
- input available
- output completed
- a timeout
- a message from another thread
As anyone who has done embedded programming will recognise, with those constructs a vast range of embedded algorithms can be simply, easily, and understandably implemented.
Talking about the NULL pointers: Please introduce a proper keyword for denoting a NULL pointer value, for example null or nullptr.
Or avoid "the billion dollar mistake" altogether. https://en.wikipedia.org/wiki/Null_pointer#History
That would be far more useful than diddling with syntax.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
Why not provide a construct that directly helps people doing low-level embedded programming?
For example, a select statement that is similar to a C switch statement:Code: [Select]select {
That statement would pause until either of the events occur and then execute the code within the relevant case. Although the select waits on several events, only one of the events would be handled by the statement when an event occurs.
case event1
// handle the event
...
break ;
case event2
// handle the event
...
break ;
}
An event could be any of:
- input available
- output completed
- a timeout
- a message from another thread
As anyone who has done embedded programming will recognise, with those constructs a vast range of embedded algorithms can be simply, easily, and understandably implemented.
Once you start to talk about events, time, resume, suspend etc then we're no longer talking language but OS.
I've been pondering the idea of attempting to create a new (compiled) programming language specifically designed for hardware programming, microcontrollers
....
So I could start by asking what people's views are here, what do you - as an MCU developer - find good...
Ada has a major blooper too as they are using parenthesis () for array index instead of brackets []. Now it is impossible to determine whether a(n) is a function call or an array element access, without checking the actual definition.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ada has a major blooper too as they are using parenthesis () for array index instead of brackets []. Now it is impossible to determine whether a(n) is a function call or an array element access, without checking the actual definition.
Yeah, I hate this kind of implicit "you have to know what X is, or have an IDE tell you" thing. I don't do C++ a lot, but when I do, not once I have mistaken function argument that is passed as reference vs. passed by value. There is difference (added &) in function definition, but at call site it looks exactly the same! Yet I think this is such large fundamental difference (whether my local variable might get modified or not) that I, as a caller, want to make that distinction, so I like the C pointer way of having to do that. (Minus points for C for the "arrays decay as pointers automagically" feature, kinda ruins it.)
Ada has a major blooper too as they are using parenthesis () for array index instead of brackets []. Now it is impossible to determine whether a(n) is a function call or an array element access, without checking the actual definition.
Yeah, I hate this kind of implicit "you have to know what X is, or have an IDE tell you" thing. I don't do C++ a lot, but when I do, not once I have mistaken function argument that is passed as reference vs. passed by value. There is difference (added &) in function definition, but at call site it looks exactly the same! Yet I think this is such large fundamental difference (whether my local variable might get modified or not) that I, as a caller, want to make that distinction, so I like the C pointer way of having to do that. (Minus points for C for the "arrays decay as pointers automagically" feature, kinda ruins it.)
This is far from clear cut. After all an array and a function are in an abstract sense the same, they are (mathematically) mappings from an input space to an output space. One might also remove an array and implement it as a function (or vice verse) in some code update, all the code would remain unchanged, no need to replace [ and ] with ( and ) all over the place.
Why does one need to care about how such a mapping is implemented when looking at code? we're putting something in and getting something out, how that's done should (IMHO) have no bearing on the code comprehension.
So I'm just not convinced myself, its an easy thing to do, altering the grammar for that is almost trivial, but you then run the risk of losing symbolic characters that might have some better use in the future, there really aren't that many standard (ASCII) symbols available.
Why not provide a construct that directly helps people doing low-level embedded programming?
For example, a select statement that is similar to a C switch statement:Code: [Select]select {
That statement would pause until either of the events occur and then execute the code within the relevant case. Although the select waits on several events, only one of the events would be handled by the statement when an event occurs.
case event1
// handle the event
...
break ;
case event2
// handle the event
...
break ;
}
An event could be any of:
- input available
- output completed
- a timeout
- a message from another thread
As anyone who has done embedded programming will recognise, with those constructs a vast range of embedded algorithms can be simply, easily, and understandably implemented.
Once you start to talk about events, time, resume, suspend etc then we're no longer talking language but OS.
Sorry, that's simply not true for multiple languages, Ada and SPARK amongst others.
Some processors have that functionality implemented in hardware. No RTOS necessary. And very good they are too for real-time embedded applications.
I you started this thread because you are interested in language features that would help embedded applications. Has that changed?
No nothing has changed, and there might be some merit in what you're suggesting, I might be wrong. My position is though that to start designing such capabilities into the language begins to impose a particular design pattern for events, threads, scheduling and so on and that might be very restrictive to the broader community of users.
Better I feel for the designer to be free to select such an important aspect of a platform and just rely on the language to help them interact with that platform, but I might be wrong, I'd need to look into this much more and I guess that's a large scope creep at this point (but rest assured we could add keywords for some of the stuff without any problems).
Why not provide a construct that directly helps people doing low-level embedded programming?
For example, a select statement that is similar to a C switch statement:Code: [Select]select {
That statement would pause until either of the events occur and then execute the code within the relevant case. Although the select waits on several events, only one of the events would be handled by the statement when an event occurs.
case event1
// handle the event
...
break ;
case event2
// handle the event
...
break ;
}
An event could be any of:
- input available
- output completed
- a timeout
- a message from another thread
As anyone who has done embedded programming will recognise, with those constructs a vast range of embedded algorithms can be simply, easily, and understandably implemented.
Once you start to talk about events, time, resume, suspend etc then we're no longer talking language but OS.
Sorry, that's simply not true for multiple languages, Ada and SPARK amongst others.
Some processors have that functionality implemented in hardware. No RTOS necessary. And very good they are too for real-time embedded applications.
I you started this thread because you are interested in language features that would help embedded applications. Has that changed?
No nothing has changed, and there might be some merit in what you're suggesting, I might be wrong. My position is though that to start designing such capabilities into the language begins to impose a particular design pattern for events, threads, scheduling and so on and that might be very restrictive to the broader community of users.
But you are already doing exactly that with the features you are proposing including/excluding from your language. Other languages also, necessarily, do that in their own ways too (Examples: statistics processing languages, modelling languages, HDLs, business-oriented languages, even spreadsheets :) )QuoteBetter I feel for the designer to be free to select such an important aspect of a platform and just rely on the language to help them interact with that platform, but I might be wrong, I'd need to look into this much more and I guess that's a large scope creep at this point (but rest assured we could add keywords for some of the stuff without any problems).
It is indeed scope creep. But then every departure from keywords and syntax into useful behaviour is also scope creep.
Ada has a major blooper too as they are using parenthesis () for array index instead of brackets []. Now it is impossible to determine whether a(n) is a function call or an array element access, without checking the actual definition.
Yeah, I hate this kind of implicit "you have to know what X is, or have an IDE tell you" thing. I don't do C++ a lot, but when I do, not once I have mistaken function argument that is passed as reference vs. passed by value. There is difference (added &) in function definition, but at call site it looks exactly the same! Yet I think this is such large fundamental difference (whether my local variable might get modified or not) that I, as a caller, want to make that distinction, so I like the C pointer way of having to do that. (Minus points for C for the "arrays decay as pointers automagically" feature, kinda ruins it.)
This is far from clear cut. After all an array and a function are in an abstract sense the same, they are (mathematically) mappings from an input space to an output space. One might also remove an array and implement it as a function (or vice verse) in some code update, all the code would remain unchanged, no need to replace [ and ] with ( and ) all over the place.
Why does one need to care about how such a mapping is implemented when looking at code? we're putting something in and getting something out, how that's done should (IMHO) have no bearing on the code comprehension.
So I'm just not convinced myself, its an easy thing to do, altering the grammar for that is almost trivial, but you then run the risk of losing symbolic characters that might have some better use in the future, there really aren't that many standard (ASCII) symbols available.
If you define brackets [] as an operator, that would keep some possibilities open for future language extensions. For example in C++ it is possible to define operator [] for a new class.
You can argue that a function call and array indexing are both mappings, but in practice (ie. the code generated by the compiler) accessing an array element is fundamentally different from a function call. In summary: using different symbols for indexing and function arguments makes good sense, and there is no reason to repeat the Ada blunder again.
This might be interesting reading: Ada 83 to Ada 2012: lessons learned over 30 years of language design
https://www.researchgate.net/profile/S-Taft/publication/286243707_Ada_83_to_Ada_2012/links/56e02f1408aec4b3333d0201/Ada-83-to-Ada-2012.pdf (https://www.researchgate.net/profile/S-Taft/publication/286243707_Ada_83_to_Ada_2012/links/56e02f1408aec4b3333d0201/Ada-83-to-Ada-2012.pdf) Unfortunately I do not have access to the text of the actual presentation.
This may be a little controversial: It is now year 2022, and modern OSs have support for Unicode character set. Basically there is no reason to use plain ASCII character set, so the language could use some symbols defined in Unicode character set. To be clear here, I do not want to see a new APL either. :)
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ha! but not without expletives!
OK since you do want to discuss this with me, let me respond.
I'm referring to the general policy for arg passing, ultimately an implementation could do this in various ways (pass in a register for example). I'm not really referring to the implementation but to the semantics, I don't think I made that clear.
So "pass by reference" here is meant to indicate that the callee can change something in the datum that's been passed. That's the default convention, and if the the caller want's to prevent a callee from changing the value of an arg, they'd simply make that arg an expression with ( and ).
This leads to increased performance because unless the developer wants to pass by value, they can write the code and just let args pass by ref, meaning passing a bunch of structures, strings, arrays or combinations of these will entail nothing more than pushing a pointer onto the stack!
procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
I have a feeling this language will suck more than what (C?) is going to replace :o :o :o
This discussion thread has some very good set of features that could be provided by the new language.
the author should really take a deep look into
the author should really take a deep look into
Frankly, the author should really get a job. I mean programming real stuff, from home automation to avionics. Only then can he come back here and talk. Otherwise, sugar crap is and will be the only main core of everything.
Note that, as we often discuss, this certainly has an impact on the intrisic qualities of a language, but little on its popularity. A number of languages that have become very popular were just a bunch of ad-hoc stuff stitched together. Sad but just how it goes.PHP (https://en.wikipedia.org/wiki/PHP) is perhaps the best example of this.
One intriguing approach would be to design a language better suited for event-based programming, suitable for use on a microcontroller. (Can you even create an event-based language without a significant runtime?) Things like interrupts do map very well to events, but I'm talking more about the internal code structure: instead of the code being based on an imperative sequence, it would be based on posting and consuming events.
Rather than syntax or grammar, one should first examine how one would implement typical tasks in such event-based code, and then compare the maintainability, robustness, and reliability of that code to the same implemented in one of the imperative languages. How obvious is the event-based code, compared to the imperative code? How many ways one can write a buggy version of the same without the bugs being obvious?
Can you even create an event-based language without a significant runtime?
Can you even create an event-based language without a significant runtime?
Probably ... no ;D
But it would be WOW!
Can you even create an event-based language without a significant runtime?
Probably ... no ;D
But it would be WOW!
Can you even create an event-based language without a significant runtime?
Probably ... no ;D
But it would be WOW!
I think this could be achieved with a relatively small runtime.
Message passing schemes is one of my "favorite" topics in programming lately, and it could be applied to passing and handling events.
Sure some hardware support would help getting better performance, but even on a classic architecture with interrupts you could implement that.
I happen to agree.Can you even create an event-based language without a significant runtime?
Probably ... no ;D
But it would be WOW!
I think this could be achieved with a relatively small runtime.
Have a look at how simple RTOSs are implemented. Ignore those that try to do everything in C, since you can't (unless you are prepared to have severe constraints with programming style).
I've used MPI a lot in both C and Fortran, but my use cases are HPC and distributed computing, and the messages contain shared simulation data.Message passing schemes is one of my "favorite" topics in programming lately, and it could be applied to passing and handling events.Message passing isn't well taught in traditional undergrad computing courses, for several reasons...
It isn't a computer science topic, cf lambda calculus, compilers, a standard algorithm.
It comes from hardware engineering.
Mainstream languages don't have it as a feature; it is relegated to libraries.
I'm not particularly familiar with ucos, but it seems to be much more than necessary for an event driven kernel (e.g. a GUI, FTP, DHCP etc).
But at least you realise your statement "Probably ... no ;D But it would be WOW!" is unduly pessimistic.
I'm not particularly familiar with ucos, but it seems to be much more than necessary for an event driven kernel (e.g. a GUI, FTP, DHCP etc).
But at least you realise your statement "Probably ... no ;D But it would be WOW!" is unduly pessimistic.
I've written a small preemptive scheduler (priority-based + per-task tick period) for my RISC-V core, which would lend itself well to event-driven stuff. One C file and one assembly file. Total object code is about 5KB with optimizations enabled. There is no specific feature for passing messages at this point, but it wouldn't be too hard to do and would probably take no more than 1 extra KB or so. I've put this on hold, but I was considering what kind of hardware blocks I could add to my core to make this more efficient. Sure there is context switching which takes a few tens of cycles. Could be avoided if I didn't make this preemptive. In order to avoid latency due to cache issues (on systems with a cache), one can define a core-coupled area of RAM dedicated to context saving/dedicated stacks/messages, so it doesn't have to go through any cache.
I understand your point about preemption, but cooperative multitasking can get messy pretty fast, and the cost of keeping context for each task can be every bit as expensive, if not more in the end, than just saving and restoring all registers. You end up with pretty intricate state machines. I've written a FAT32/exFAT library in this cooperative way (in order to have no blocking call whatsoever), and it was a real PITA.
And if you stick to messages/events as synchronization means, then a lot of the hard stuff about synchronization becomes a lot easier.
Meeting deadlines is also easier in general with preemption, as long as priorities and tick periods are handled appropriately (so not in the way general-purpose schedulers usually do.)
But at least you realise your statement "Probably ... no ;D But it would be WOW!" is unduly pessimistic.
...
which uses a state object to record its own state, and stack only for calling functions;
...
From time to time I have been missing a [C] preprocessor that would be able to generate hygienic macros, and would be coupled to the compiler more tightly so that the macros could determine and check the types of the macro parameters. In that way it would be possible to extend the core language, and for example create framework for implementing events, state machines, patterns, and templates without making the core language too complex.
...
which uses a state object to record its own state, and stack only for calling functions;
...
That's a very good technique, especially where there are many independent FSMs running simultaneously, e.g. telecom phone calls, or web connections.
Have a single FIFO containing events yet to be processed. Have one "worker thread" per core/processor dedicated to consuming an event, doing the actions, and creating another event. Each worker thread sucks the next event from the FIFO. Doesn't require co-routines. It all leads to good scaleable high performance applications.
I hate that kind of thing :)
It also means it is best to pass data by value in registers, so that data already in registers does not have to be written to RAM just to call a function.
Passing data by reference is an utter pain.
[C] preprocessor that would be able to generate hygienic macros
I hate that kind of thing :)
macmini-G4 /projects/devchain-baremetal-2022 # ./mybuild info all
m6811-elf, binutils v2.23, gcc v3.3.6-s12x
m88k-coff, binutils v2.16.1, gcc v2.95.3
m68k-elf, binutils v2.34, gcc v4.1.2-core
mips-elf, binutils v2.24, gcc v4.1.2-core
mips64-elf, binutils v2.24, gcc v4.1.2-core
powerpc-elf, binutils v2.24, gcc v4.1.2-core
powerpc-eabi, binutils v2.24, gcc v4.1.2-core
arm-eabi, binutils v2.24, gcc v4.1.2-core
sh2-elf, binutils v2.24, gcc v4.1.2-core
bugged parts: cpp0, cpp, collect....
which uses a state object to record its own state, and stack only for calling functions;
...
That's a very good technique, especially where there are many independent FSMs running simultaneously, e.g. telecom phone calls, or web connections.
Have a single FIFO containing events yet to be processed. Have one "worker thread" per core/processor dedicated to consuming an event, doing the actions, and creating another event. Each worker thread sucks the next event from the FIFO. Doesn't require co-routines. It all leads to good scaleable high performance applications.
Miro Samek's freely available ebook Practical UML Statecharts in C/C++, 2nd Ed Event-Driven Programming for Embedded Systems
https://www.state-machine.com/psicc2 (https://www.state-machine.com/psicc2)
describes an Active Object pattern decoupling the event producers and consumers:
https://www.state-machine.com/active-object (https://www.state-machine.com/active-object)
This Active Object-pattern can be used as an alternative to / with the system-wide event-queue.
Or to an array, yes.It also means it is best to pass data by value in registers, so that data already in registers does not have to be written to RAM just to call a function.Yup. The only good use is for objects, so you pass the pointer to a large structure.
Passing data by reference is an utter pain.
If you look at efficient service daemons in Linux and Unix systems, nonblocking I/O using select()/poll() does something extremely similar:
Each connection is their own state object, and there is a simple "event" loop, handling all descriptors that are readable/writable in turn, and otherwise blocking in the select()/poll() call.
Expressing this pattern in a better way would be nice. This is also what I meant by having more than one event loop.
Python passes by assignment, which basically means that ints, floats, strs, tuples, and other immutable types are passed by value, but dictionaries, lists, sets, and mutable types and objects are passed by reference. It doesn't seem to be a problem for Python programmers, which indicates to me that the passing scheme (by value, by reference, or by assignment like in Python) doesn't seem to significantly affect the code; so simplicity should be favoured for the reasons I already outlined.
But at least you realise your statement "Probably ... no ;D But it would be WOW!" is unduly pessimistic.
I mean I don't think I am good enough to make my-c an event-oriented language.
do you really need to design a specific language for this? Apart from the sugar coating?
do you really need to design a specific language for this? Apart from the sugar coating?
I think the right question is: how can I reduce the amount of - and tricks behind the stage - aka assembly code - I have to write to serve events-programming in a language?
If we concentrate on the main "worker" engine doing most of the processing, and ignore separate event loops which create events....Excellent questions. I don't know the answers, and the only way to find out I know of is to look at actual practical implementations.
Is there any performance value in having more than one event loop per core?
Is there any simplicity/clarity value in having more than one event loop per core?
I tend to ignore Python where high performance and scalability is required, since despite having "threads" it is "crippled" by its Global Interpreter Lock. Ditto Ruby. That may be unjust, but...Oh, I only meant wrt. syntax: that whether parameters are passed by value or by reference (or by assignment, a "mix" of the two, as in Python), does not seem to affect the number of bugs in the code.
If we concentrate on the main "worker" engine doing most of the processing, and ignore separate event loops which create events....Excellent questions. I don't know the answers, and the only way to find out I know of is to look at actual practical implementations.
Is there any performance value in having more than one event loop per core?
Is there any simplicity/clarity value in having more than one event loop per core?
In my opinion, this means that because at the hardware level it makes a HUGE difference, passing by value being much, MUCH more efficient, passing by value is the superior approach for low-level languages.Don't you need both? Although I write assembler, I would think passing data in a higher level language would be similar. I have arithmetic stack operations, the two relevant subroutines to get stuff on the stack are:
CALL PushLit
12345
andCALL PushU2
16bitUnsigned ; pointer to the value
So both pass by value and pass by reference.CALL PrintChar ; value is in a register
andCALL PrintStr
PointToString
If your language has a concept of pointer, then by constructing a pointer to an object and passing that pointer by value will give you the effects of passing by reference.In my opinion, this means that because at the hardware level it makes a HUGE difference, passing by value being much, MUCH more efficient, passing by value is the superior approach for low-level languages.Don't you need both?
Although passing by value is efficient in terms of CPU cycles, it isn't the whole picture. Passing by reference has CPU overhead but less instruction memory.No, that claim does not seem to be supported by practical evidence.
If your language has a concept of pointer, then by constructing a pointer to an object and passing that pointer by value will give you the effects of passing by reference.You are right. If you take a pointer to be a value, then I pass everything by value.
There are 8-bit architectures that have very few registers and were designed to pass function arguments on the stack, but they're quite difficult to optimize code for. Most hardware today, from AVRs to ARMs to RISCV, have many general-purpose registers, so targeting those makes more sense to me anyway.
There are 8-bit architectures that have very few registers and were designed to pass function arguments on the stack
Sun UltrasSPARC
Sun UltrasSPARC
My reference is POWER10. With built-in tr-mem.
tr-mem costs silicon, thus money to us, POWER10 is *VERY* expensive, but ... I personally think it's worth it.
I mean, you'd better use silicon for tr-mem rather than for sliding registers window, plus complex out-of-order-reordering load/store units (that goes crazy, very crazy with specific pipeline instructions, which costs more silicon, more complexity in both hardware and software sides)
Comparing the SysV ABI (application binary interface, at the hardware level, assembly language) on x86-64 to most common x86 ABIs shows that passing by value, using registers to store values, and returning up to two 64-bit words, yields superior performance (regardless of language) because it minimises call overhead.
One fundamental problem with transactional memory is that doesn't scale
[...]
it worked fine with 4 chips, but despite best attempts it never worked well above that.
One fundamental problem with transactional memory is that doesn't scale
[...]
it worked fine with 4 chips, but despite best attempts it never worked well above that.
ummm, talking about my MIPS5++ experimental board, the CPU module has only 4 cores attached to a single tr-mem.
POWER10 requires a special Talos mainboard, different from the the first modern (post-2013) you can buy for POWER9 workstations. and servers, and the last POWER9 TL2WK2 comes with Two 4-core IBM POWER9 v2 CPUs.
Perhaps *only 4 cores* per package are too few. I need a bigger workstation :-//
Another fundamental problem with transactional memory is what happens when a transactions are used inappropriately or a transaction fails to complete. That can be due to programming errors, memory errors, processor errors etc. Even if timeouts are implemented and work, how much unrelated activity is screwed until then?
Another fundamental problem with transactional memory is what happens when a transactions are used inappropriately or a transaction fails to complete. That can be due to programming errors, memory errors, processor errors etc. Even if timeouts are implemented and work, how much unrelated activity is screwed until then?
Well, I do think 12 general purpose registers (xCORE-200 XS2 ISA (PDF) (https://www.xmos.ai/download/xCORE-200:-The-XMOS-XS2-Architecture-%28ISA%29%281.1%29.pdf)) is plenty! ^-^There are 8-bit architectures that have very few registers and were designed to pass function arguments on the stack, but they're quite difficult to optimize code for. Most hardware today, from AVRs to ARMs to RISCV, have many general-purpose registers, so targeting those makes more sense to me anyway.Not always true...
That was demonstrated very clearly with the Sun UltrasSPARC Niagara T series processors, which had up to 128 "cores" (and used them effectively!) in 2005-2011, when x86-64 has <8 cores.Yep. I personally like the idea of asymmetric multiprocessing a lot, and would love to play with such hardware. Alas, the only ones I have are mixed Cortex-A cores (ARM big.LITTLE (https://en.wikipedia.org/wiki/ARM_big.LITTLE)).
If the values are passed on stack, they have to be loaded from the stack before they're operated on. This is the overhead one can avoid by passing by value in registers, with passing by reference implemented via pointers.
replacing the standard C library with something more suitable for my needs
Is this is going off topic. Xeon processors, MIPS, Athlon, power10?
Replace it with my_c.lib :-DD :o :o :o
replacing the standard C library with something more suitable for my needs
That's my plan for 2023 for m68k.
Everything replaced from crt0 up.
We can do it ;D
I'm thinking a microcontroller has less CPU grunt but is augmented by a few onboard PWMs, extra timers, voltage references, comparators, ADC and DAC ability, USARTs, onboard USB, I2C, SPI, WDT, writable permanent memory, sleep modes and commonly low powered so it can be run off battery power. There may be some offloading of functions such as DMA and gated timers. And commonly just 1 CPU.Yes.
How do you then optimize for different architectures - AVR with multiple registers but transfers to memory are load and store - PIC with one working register but instructions like INCFSZ (increment memory and skip if zero) that doesn't touch the register at all.That's exactly why I said I would focuse on the multiple general purpose register architectures: AVR on 8-bit, ARM/RISC-V/x86-64 on 32- and 64-bit architectures.
Perhaps the best course is to wait for Sherlock to produce the language, and see if what benefits (if any) it brings. And with something to work with, then make comment to improve (if feasible).My beef is that they're taking an already well-trodden path that has not thus far yielded anything practically useful. Why repeat that? I'm not saying this because I hope to benefit from their work, I am saying this because I've seen too many people get frustrated and fail, and I don't want Sherlock to do so too.
It is less work than one might expect, actually, at least for hosted environments ("userspace" code, running under a kernel providing I/O).replacing the standard C library with something more suitable for my needsThat's my plan for 2023 for m68k.
Everything replaced from crt0 up.
Transactional memory and atomic memory access is discussed here, because it is a very similar problem on larger, multi-core processors. There are three basic ways of implementing atomic memory access:Higher-level primitives like mutexes, semaphores, condition variables are implemented in terms of the above atomic operations. However, there are also a large number of lockless data structures that rely on atomic access, and they tend to differ a bit between CAS and LL/SC.
- Transactional memory. Writes to transactional memory are grouped, and only actually committed to the underlying memory (atomically) if there are no conflicts.
- CAS: Compare-exchange. The basic operation is a conditional compare-exchange, that atomically replaces the memory contents if the previous contents match a given value.
- LL/SC: Load-linked, store-conditional. The basic operation is a linked load, which basically "claims" ownership of that memory address. Within a few instructions (often with restrictions like no jumps), a corresponding linked store to that address will succeed if the contents were not modified in between.
I'm sure you can imagine how problematic it is to expose the low-level atomic operations in a low-level programming language: if you expose just CAS, the code will be suboptimal on LL/SC architectures, and vice versa. If you provide both, how is the developer going to decide which one to use?
You're right, as usual. :)Transactional memory and atomic memory access is discussed here, because it is a very similar problem on larger, multi-core processors. There are three basic ways of implementing atomic memory access:Higher-level primitives like mutexes, semaphores, condition variables are implemented in terms of the above atomic operations. However, there are also a large number of lockless data structures that rely on atomic access, and they tend to differ a bit between CAS and LL/SC.
- Transactional memory. Writes to transactional memory are grouped, and only actually committed to the underlying memory (atomically) if there are no conflicts.
- CAS: Compare-exchange. The basic operation is a conditional compare-exchange, that atomically replaces the memory contents if the previous contents match a given value.
- LL/SC: Load-linked, store-conditional. The basic operation is a linked load, which basically "claims" ownership of that memory address. Within a few instructions (often with restrictions like no jumps), a corresponding linked store to that address will succeed if the contents were not modified in between.
I'm sure you can imagine how problematic it is to expose the low-level atomic operations in a low-level programming language: if you expose just CAS, the code will be suboptimal on LL/SC architectures, and vice versa. If you provide both, how is the developer going to decide which one to use?
You've missed a class: Atomic Memory Operations, as seen in a number of current ISAs.
This seems on the face expensive and not scalable, but Berkeley's "TileLink" bus (enhanced and used by SiFive) enables the memory address, a constant, and the operation to be performed to be sent out to a suitably intelligent node in the memory system -- often the L2 cache shared by a number of CPU cores, but it might even be an IO device -- and the ALU operation is performed right where the data is stored, and only the result sent back to the CPU (and only if it's not going to be ignored by being written to register X0).Extremely interesting!
Well, I do think 12 general purpose registers (xCORE-200 XS2 ISA (PDF) (https://www.xmos.ai/download/xCORE-200:-The-XMOS-XS2-Architecture-%28ISA%29%281.1%29.pdf)) is plenty! ^-^There are 8-bit architectures that have very few registers and were designed to pass function arguments on the stack, but they're quite difficult to optimize code for. Most hardware today, from AVRs to ARMs to RISCV, have many general-purpose registers, so targeting those makes more sense to me anyway.Not always true...
But, sure, there are exceptions to any rule of thumb. XCore is definitely one.
That was demonstrated very clearly with the Sun UltrasSPARC Niagara T series processors, which had up to 128 "cores" (and used them effectively!) in 2005-2011, when x86-64 has <8 cores.Yep. I personally like the idea of asymmetric multiprocessing a lot, and would love to play with such hardware. Alas, the only ones I have are mixed Cortex-A cores (ARM big.LITTLE (https://en.wikipedia.org/wiki/ARM_big.LITTLE)).
We can see from the increasing complexity of peripherals on even cheap ARM Cortex-M microcontrollers, and things like Raspberry Pi Pico/RP2040 (and of course TI AM335x with its PRUs) that we're slowly going that way anyway.
Personally, I'd love to have tiny programmable cores with just a basic ALU – addition, subtraction, comparison, and bit operations, with just a couple of run-time registers – and access to reading and writing from the main memory, and the ability to raise an interrupt in the main core. Heck, all the buses I use could be implemented with one.
I looked at XEF216-512-TQ128-C20A (23.16€ in singles at Digikey, in stock), and the problem I have with it is that it is too powerful! :o
I fully understand why XMOS developed xC, a variant of C with additions for tile computing; something like this would be perfect for developing a new event-based programming language, since it already has the hardware support for it.
For now, however, I have my own sights set much lower: replacing the standard C library with something more suitable for my needs, to be used in a mixed freestanding C/C++ environment. This is well defined in the standards, and there are more than one compiler I can use it with (specifically, GCC and Clang on different architectures, including 8-bit (AVR), 32-bit (ARM, x86), and 64-bit (x86-64, Aarch64/ARM64) ones).
...
As you can see, you really need to understand the hardware differences to be able to determine what kind of language approach is suitable on use with a MCU.
...
And why go for just another object-oriented imperative language, when there is good reason to expect better returns from say an event-based language – simply because there are decades of research and a lot of money behind existing object-oriented imperative languages like Rust, but very few people considering event-based programming languages, even though a lot of code we write for microcontrollers revolves around dealing with events?
I for one am not willing to just stand by and look silently when someone sets themselves up for a failure. I will pipe up, even if they themselves would prefer me to stay quiet. It's a personality flaw.
This seems on the face expensive and not scalable, but Berkeley's "TileLink" bus (enhanced and used by SiFive) enables the memory address, a constant, and the operation to be performed to be sent out to a suitably intelligent node in the memory system -- often the L2 cache shared by a number of CPU cores, but it might even be an IO device -- and the ALU operation is performed right where the data is stored, and only the result sent back to the CPU (and only if it's not going to be ignored by being written to register X0).
This scales extremely well.
Arm added a similar capability in AMBA 5 CHI (Coherent Hub Interface)
I'm not a fan of asymmetric processing for applications which aren't nailed down when the device ships. The asymmetry introduces a nasty question of whether a particular operation should be done here or there. That was a downfall of the CELL(?) architecture with one BIG and 8(?) LITTLEs a decade ago.Ah, yes: IBM Cell (https://en.wikipedia.org/wiki/Cell_(microprocessor)) architecture, used on the Sony Playstation 3.
Where the only operations that will be in LITTLE are fixed at design time, that disadvantage is not a major problem.I'm specifically talking about small "accelerators" with tiny instruction sets, basically a simple ALU (I don't even need multiplication or division myself!), attached to e.g. a DMA engine, GPIO ports, that sort of a thing; not full asymmetric cores, and not with full or random memory access.
static const struct sock_filter strict_filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof (struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP | BPF_JEQ, SYS_rt_sigreturn, 5, 0), // Jump to RET_ALLOW if match
BPF_JUMP(BPF_JMP | BPF_JEQ, SYS_read, 4, 0), // Jump to RET_ALLOW if match
BPF_JUMP(BPF_JMP | BPF_JEQ, SYS_write, 3, 0), // Jump to RET_ALLOW if match
BPF_JUMP(BPF_JMP | BPF_JEQ, SYS_exit, 2, 0), // Jump to RET_ALLOW if match
BPF_JUMP(BPF_JMP | BPF_JEQ, SYS_exit_group, 1, 0), // Jump to RET_ALLOW if match
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
};
static const struct sock_fprog strict = {
.len = (unsigned short)( sizeof strict_filter / sizeof strict_filter[0] ),
.filter = (struct sock_filter *)strict_filter
};
I'm not a fan of asymmetric processing for applications which aren't nailed down when the device ships. The asymmetry introduces a nasty question of whether a particular operation should be done here or there. That was a downfall of the CELL(?) architecture with one BIG and 8(?) LITTLEs a decade ago.Ah, yes: IBM Cell (https://en.wikipedia.org/wiki/Cell_(microprocessor)) architecture, used on the Sony Playstation 3.Where the only operations that will be in LITTLE are fixed at design time, that disadvantage is not a major problem.I'm specifically talking about small "accelerators" with tiny instruction sets, basically a simple ALU (I don't even need multiplication or division myself!), attached to e.g. a DMA engine, GPIO ports, that sort of a thing; not full asymmetric cores, and not with full or random memory access.
Stuff like CRC or hash calculation while doing memory-to-memory DMA, peripheral bus implementations, PWM, PDM, ADC with averaging and min-max recording, even wide LUT.
This seems on the face expensive and not scalable, but Berkeley's "TileLink" bus (enhanced and used by SiFive) enables the memory address, a constant, and the operation to be performed to be sent out to a suitably intelligent node in the memory system -- often the L2 cache shared by a number of CPU cores, but it might even be an IO device -- and the ALU operation is performed right where the data is stored, and only the result sent back to the CPU (and only if it's not going to be ignored by being written to register X0).
This scales extremely well.
Arm added a similar capability in AMBA 5 CHI (Coherent Hub Interface)
I don't know the TileLink bus, but at that level it sounds similar to the hypertransport comms in AMD's Athlon/Opteron/etc. That works nicely up to a certain point and the cache coherence traffic and latency becomes a limiting factor.
By and large trying to offload i/o onto a separate processor only works in some cases; in many (most?) it is a problem.No, actually neither. And I'm very often fighting buffer bloat, so I'm looking to ways of avoid that, too.
[...]
If all you are attempting is to limit the unpredictability of i/o operations' timing, then a separate i/o processor can work well. And with that we're back to xCORE!
The idea with the small ALUs is not to have more buffers, but to do whatever small arithmetic and bit operations are necessary when the data is accessed anyway, and use as few buffers as is possible. Zero-copy operation, really, is what I'm after.
Two intractable problems are:
- the network processor is slower...
- ...waiting for the (slow) network processor...
Even if you have just basic 32-bit add and bitwise operations plus branch instructions, running at say 200MHz while the more complex main CPU runs heavily pipelined at 400MHz, that's OK. If you look at current high-end microcontroller offerings, their IO and most peripherals already run at Fcpu/2 or lower. One could just replace the hardwired peripherals with tiny, simple CPUs.I agree. Furthermore, these tiny cores – really, just ALUs – don't even need arbitrary memory address access, only a couple of accumulator registers (although a dedicated read-only lookup access would be nice), and very limited loop/jump/conditional capabilities. They're truly simple.
these tiny cores
...
Still, it is the programming of such tiny limited cores in a sub-programming-language within larger applications that currently keeps them in "niche" uses, I believe.
...
Something like 100 MHz would suffice for my own peripheral bus needs quite nicely (even when the MCU core itself runs at say 400-1000 MHz).
Still, it is the programming of such tiny limited cores in a sub-programming-language within larger applications that currently keeps them in "niche" uses, I believe.
I know tggzzz and others somewhat disagree, but I've found them (as I've described, from BPF to shaders) extremely powerful, and only difficult to "teach" others to use efficiently and effectively. (Not the programming part itself; but the design and integration part.)
In my opinion, it is exactly the development of programming paradigms –– especially event-based programming languages –– that is keeping us software folks back, when compared to the progress made on the hardware side in the last three decades or so. I mean, compilers have become cleverer and better, and we have more choices when it comes to programming languages and especially libraries, but real forward development is rare!
It is also why I do not want to discourage people like OP who want to design new languages; I only want them to consider approaches that haven't already been trodden by scores of well-resourced developers without real significant results; to see approaches that my and others' experience indicates might yield much better results, something new.
(Okay, I am actually well aware that the OP does not read any of my posts, so I'm not writing these posts to OP. I'm writing these to the other people who are mulling new microcontroller programming language development now or in the future, in the hopes that the concepts I'm pushing leads them to discover and develop something truly better than the imperative languages we have now, without complicated layers of abstractions and runtimes that require more and more computing resources like we see on server and desktop architectures.)
This seems on the face expensive and not scalable, but Berkeley's "TileLink" bus (enhanced and used by SiFive) enables the memory address, a constant, and the operation to be performed to be sent out to a suitably intelligent node in the memory system -- often the L2 cache shared by a number of CPU cores, but it might even be an IO device -- and the ALU operation is performed right where the data is stored, and only the result sent back to the CPU (and only if it's not going to be ignored by being written to register X0).
This scales extremely well.
Arm added a similar capability in AMBA 5 CHI (Coherent Hub Interface)
I don't know the TileLink bus, but at that level it sounds similar to the hypertransport comms in AMD's Athlon/Opteron/etc. That works nicely up to a certain point and the cache coherence traffic and latency becomes a limiting factor.
It's not cache-coherence. The whole point is to be able to avoid moving cache lines around.
Everything works up to a certain point. The question to be answered is whether that point is 4 CPUs, 64 CPUs, or 1024+ CPUs.
x86's (and SPARC's) TSO memory model is also a limiting factor for those systems. Arm and RISC-V have better memory model, certainly by the time the CPU count gets into three figures.
Note that there are already RISC-V chips in production with more than 1000 cores e.g. ET-SoC-1 with 1088 small in-order cores with vector units, plus 4 big OoO cores.
Yet, I'm not particularly enamored by event-based programming. For things like the ALU microcores mentioned in previous messages – including BPF, pixel and vertex shader programming –, I definitely prefer imperative programming.
It interests me because I see it as a possible way out of the decades-long stuck phase in software development! (Okay, object oriented programming has made user interface programming much, much easier and arguably more effective than before, but it is just about the only field I've seen truly progress forward; and yet, even it hasn't made any big difference in software reliability, effectiveness –– and definitely has worsened memory hogging.)
In other words, I see us software folks stuck in abstractions and paradigms that haven't really evolved much in the last three decades or more, and am trying to get others interested in the largely untested approaches, because the imperative-object-oriented approaches don't seem to be able to produce anything clearly better, even with decades of person-hours of development work in them.
if you think C++ is the answer,
then you need to revisit the question!
Quoteif you think C++ is the answer,
then you need to revisit the question!
Loooooooool, I want a t-shirt with this :D :D :D
C++ is great, because C++ connects people: basically all experienced or knowledgeable people (people with a clue, or those who grok, so to speak) agree on if not hating, but at least not loving C++, no matter how much they disagree otherwise. It's truly a horrible and messy language.
C++ is great, because C++ connects people: basically all experienced or knowledgeable people (people with a clue, or those who grok, so to speak) agree on if not hating, but at least not loving C++, no matter how much they disagree otherwise. It's truly a horrible and messy language.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ha! but not without expletives!
OK since you do want to discuss this with me, let me respond.
I'm referring to the general policy for arg passing, ultimately an implementation could do this in various ways (pass in a register for example). I'm not really referring to the implementation but to the semantics, I don't think I made that clear.
So "pass by reference" here is meant to indicate that the callee can change something in the datum that's been passed. That's the default convention, and if the the caller want's to prevent a callee from changing the value of an arg, they'd simply make that arg an expression with ( and ).
This leads to increased performance because unless the developer wants to pass by value, they can write the code and just let args pass by ref, meaning passing a bunch of structures, strings, arrays or combinations of these will entail nothing more than pushing a pointer onto the stack!
Ugh.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
the author should really take a deep look into
Frankly, the author should really get a job. I mean programming real stuff, from home automation to avionics. Only then can he come back here and talk. Otherwise, sugar crap is and will be the only main core of everything.
Why do you want to discuss me rather than programming languages?
Your idea have ZERO real life experience, too much arrogance.
Enjoy my ignore list.
Why do you want to discuss me rather than programming languages?
Run it and it and see, then tell us the answer and then explain what the relevance is of asking this question? is it an attempt to make some "point"? well then state your case, state your proposition whatever it is.
C++ is great, because C++ connects people: basically all experienced or knowledgeable people (people with a clue, or those who grok, so to speak) agree on if not hating, but at least not loving C++, no matter how much they disagree otherwise. It's truly a horrible and messy language.
As I mentioned in other threads, one particularly entertaining activity (up to a point, don't abuse it) is to watch talks from C++ conferences such as CppCon.
Almost endless hours of fun.
And to be fair, although for slightly different reasons, Rust talks are often pretty entertaining too. Don't take my word for it, go watch a few!
Why do you want to discuss me rather than programming languages?Run it and it and see, then tell us the answer and then explain what the relevance is of asking this question? is it an attempt to make some "point"? well then state your case, state your proposition whatever it is.
The Dunning-Kruger effect occurs when a person's lack of knowledge and skills in a certain area
(real life programming) cause them to overestimate their own competence.
Enjoy my ignore list.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ha! but not without expletives!
OK since you do want to discuss this with me, let me respond.
I'm referring to the general policy for arg passing, ultimately an implementation could do this in various ways (pass in a register for example). I'm not really referring to the implementation but to the semantics, I don't think I made that clear.
So "pass by reference" here is meant to indicate that the callee can change something in the datum that's been passed. That's the default convention, and if the the caller want's to prevent a callee from changing the value of an arg, they'd simply make that arg an expression with ( and ).
This leads to increased performance because unless the developer wants to pass by value, they can write the code and just let args pass by ref, meaning passing a bunch of structures, strings, arrays or combinations of these will entail nothing more than pushing a pointer onto the stack!
Ugh.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
Run it and it and see, then tell us the answer and then explain what the relevance is of asking this question? is it an attempt to make some "point"? well then state your case, state your proposition whatever it is.
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ha! but not without expletives!
OK since you do want to discuss this with me, let me respond.
I'm referring to the general policy for arg passing, ultimately an implementation could do this in various ways (pass in a register for example). I'm not really referring to the implementation but to the semantics, I don't think I made that clear.
So "pass by reference" here is meant to indicate that the callee can change something in the datum that's been passed. That's the default convention, and if the the caller want's to prevent a callee from changing the value of an arg, they'd simply make that arg an expression with ( and ).
This leads to increased performance because unless the developer wants to pass by value, they can write the code and just let args pass by ref, meaning passing a bunch of structures, strings, arrays or combinations of these will entail nothing more than pushing a pointer onto the stack!
Ugh.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
Run it and it and see, then tell us the answer and then explain what the relevance is of asking this question? is it an attempt to make some "point"? well then state your case, state your proposition whatever it is.
I can not run it, sir.
This is my best guess of a possible syntax for YOUR proposed programming language, which you have not provided us with a compiler for.
My proposition is that your feature of everything is pass by reference not only sucks in general, but is especially nasty when combined with your feature of nested functions.
I have a feeling this language will suck more than what (C?) is going to replace :o :o :o
Note that, as we often discuss, this certainly has an impact on the intrisic qualities of a language, but little on its popularity. A number of languages that have become very popular were just a bunch of ad-hoc stuff stitched together. Sad but just how it goes.PHP (https://en.wikipedia.org/wiki/PHP) is perhaps the best example of this.
Considering that, why would anyone start designing a language from the ground up, starting with syntax or grammar, instead of examining how expressions and algorithms would be best expressed in the new language?
I wondered if there was a sensible answer.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
I wondered if there was a sensible answer.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
Hopefully the compiler would give a warning "Do you really want to create a local variable the same name as a passed parameter?" at line 2.
If that warning is ignored, then does procedure bar inherit the current instantiation of i from foo due to scoping?
if not, then compiler should warn "uninitiated variable i"
if so within bar, i := i + 1; will operate on the i declared in foo due to scoping: i then = 2
j := j + 1 will operate on the i declared in foo as a passed variable by reference: i then = 3
Print 3
This seems logical to me. Makes what I regard as sensible interpretation of scoping. Is this a reasonable analysis? other interpretation?
Maybe a solution is to make the rules flexible [procedure bar(i) - don't say how i is passed and let the linker make that decision. Not the same as VB where the variable is mutable at run time, just flexible until link time]. If the procedure caller doesn't care what happens to the passed variables or requires their version to remain unchanged, can explicitly pass by value. If the procedure caller expects something to be modified and wants the result, explicitly pass by reference. Let the linker fix it up. This means the programmer has more flexibility, the procedure writer can determine if a particular method is required (causing a linker error if not adhered to) or let the linker fix it. And there is no ambiguity in the calling code.
I wondered if there was a sensible answer.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
Hopefully the compiler would give a warning "Do you really want to create a local variable the same name as a passed parameter?" at line 2.
If that warning is ignored, then does procedure bar inherit the current instantiation of i from foo due to scoping?
if not, then compiler should warn "uninitiated variable i"
if so within bar, i := i + 1; will operate on the i declared in foo due to scoping: i then = 2
j := j + 1 will operate on the i declared in foo as a passed variable by reference: i then = 3
Print 3
This seems logical to me. Makes what I regard as sensible interpretation of scoping. Is this a reasonable analysis? other interpretation?
Maybe a solution is to make the rules flexible [procedure bar(i) - don't say how i is passed and let the linker make that decision. Not the same as VB where the variable is mutable at run time, just flexible until link time]. If the procedure caller doesn't care what happens to the passed variables or requires their version to remain unchanged, can explicitly pass by value. If the procedure caller expects something to be modified and wants the result, explicitly pass by reference. Let the linker fix it up. This means the programmer has more flexibility, the procedure writer can determine if a particular method is required (causing a linker error if not adhered to) or let the linker fix it. And there is no ambiguity in the calling code.
Very good except our friend stated "So 'pass by reference' here is meant to indicate that the callee can change something in the datum that's been passed", and in other places that it might not actually be by a pointer, but by being passed in a register and passed back. He's also indicated several times the he follows Ada in various things.
Ada has INOUT parameters, where it is *undefined* whether they are passed by copy/return or by pointer.
This makes a difference to code such as the above.
for (I = 0; I < Funct(I); I++)
{
;
}
In a different discussion about another language the question of how best to exit or continue loops, particularly nested loops, came up for discussion.
What are people's thoughts about this kind of thing?
Rows: for (i = 0; i < n; i++)
{
Columns: for (j = 0; j < m; j++)
{
...
if (...)
exit Rows;
}
}
In a different discussion about another language the question of how best to exit or continue loops, particularly nested loops, came up for discussion.
What are people's thoughts about this kind of thing?
My take on this is named blocks, which generalizes loops.
Then to exit the block, 'exit xxx' or 'break xxx' or whatever syntax you like.
Example (again the syntax is just an example C-like, use whatever syntax you like - this is NOT C, so those tags would not be C goto labels, just block names):Code: [Select]Rows: for (i = 0; i < n; i++)
{
Columns: for (j = 0; j < m; j++)
{
...
if (...)
exit Rows;
}
}
The block names or tags could be used for just any code block and not just loops. And again the syntax is just to show the idea, could be something else entirely. The idea is just to give code blocks a tag.
Simple and effective, and easy to read IMO. (Obviously you can do the same in C using goto and labels, but this isn't as readable IMO, and the above may allow better static analysis and better optimization.)
sort_data:
procedure (arg);
// stuff
end;
label outer_loop;
loop while (a > b)
// stuff
if (X) then
leave outer_loop; // break in C
end;
{main_loop}
loop
// stuff
if (a > b) then
leave main_loop;
end;
Yes, all args are passed by ref. To achieve pass by value (that is, pass a ref to a temp copy) one just surrounds the arg in parens:
omfg. I am without words.
You just threw away a huge chunk of performance, as well as compatibility with all existing libraries and OS interfaces.
Ha! but not without expletives!
OK since you do want to discuss this with me, let me respond.
I'm referring to the general policy for arg passing, ultimately an implementation could do this in various ways (pass in a register for example). I'm not really referring to the implementation but to the semantics, I don't think I made that clear.
So "pass by reference" here is meant to indicate that the callee can change something in the datum that's been passed. That's the default convention, and if the the caller want's to prevent a callee from changing the value of an arg, they'd simply make that arg an expression with ( and ).
This leads to increased performance because unless the developer wants to pass by value, they can write the code and just let args pass by ref, meaning passing a bunch of structures, strings, arrays or combinations of these will entail nothing more than pushing a pointer onto the stack!
Ugh.Code: [Select]procedure foo(i : integer)
var i : integer = 1;
procedure bar(j : integer)
i := i + 1;
j := j + 1
end
bar(i);
print(i)
end
What is printed?
Run it and it and see, then tell us the answer and then explain what the relevance is of asking this question? is it an attempt to make some "point"? well then state your case, state your proposition whatever it is.
I can not run it, sir.
This is my best guess of a possible syntax for YOUR proposed programming language, which you have not provided us with a compiler for.
My proposition is that your feature of everything is pass by reference not only sucks in general, but is especially nasty when combined with your feature of nested functions.
FYI - The language has a name now, it has been named "Imperium" after the Latin term for "control", source files are suffixed .ipl - Imperium Programming Language.Be careful, this might cost you a life sentence in Azkaban.
FYI - The language has a name now, it has been named "Imperium" after the Latin term for "control", source files are suffixed .ipl - Imperium Programming Language.
FYI - The language has a name now, it has been named "Imperium" after the Latin term for "control", source files are suffixed .ipl - Imperium Programming Language.
Usually the name is chosen to reflect a language's principal characteristics and behaviour.
Congratulations on making this important advance which will, no doubt about it, speed up progress.
Here's a condensed list of important goals for a new language suitable for writing code for MCUs:I have been promoting and supporting the use of Oberon-07 (https://people.inf.ethz.ch/wirth/Oberon/Oberon07.Report.pdf) as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe. I have checked it against your list of required features as follows:
1. Extensible grammar (new keywords easily added over time, 100% backward compatibility guaranteed)
2. Elimination of pointer arithmetic.
3. Multidimensional arrays with variable upper/lower bounds.
4. An advanced preprocessor.
5. Support true 'offsets' in addition to conventional 'pointers'.
6. Supported nested procedures.
7. Support exceptions/conditions
8. Support 'builtin' and 'intrinsic' as fundamental language concepts.
9. Abstraction of interop - use a declarative means of interfacing to code written by other languages.
10. Support 'bit' as a data type.
11. Support flexible alignment, padding and ordering of structure/array members.
12. Support endianness operations for converting a contiguous memory block from big/little to little/big endian.
13. Support multiple 'heaps' and facilitate the freeing of all allocations within a heap by simply resetting the heap.
14. Innate support for coroutines.
15. Support decimal, octal, binary and hex numeric literals.
16. Support fixed point arithmetic in decimal/binary base.
17. Remain vigilante, aware of other old and new languages like Ada, Rust, Zig, Hare, Odin etc, where are these weak? where are they strong?
Is there anything fundamental missing from this list ? (I recognize that some want to talk about threading, synchronization and multiple cores, these are important but perhaps best set aside for now, to be revisited later).
It would be interesting to also examine various examples of C based solutions to certain MCU problems to see how a new language could improve the implementation of that code.
I'd like to also remind some here that all/any of these topics are up for discussion, I am not only concerned with grammar, if anyone wants to discuss any of these aspects then please be my guest, lets explore and move forward...
I have been promoting and supporting the use of Oberon-07 as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe.
Here's a condensed list of important goals for a new language suitable for writing code for MCUs:I have been promoting and supporting the use of Oberon-07 (https://people.inf.ethz.ch/wirth/Oberon/Oberon07.Report.pdf) as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe. I have checked it against your list of required features as follows:
1. Extensible grammar (new keywords easily added over time, 100% backward compatibility guaranteed)
2. Elimination of pointer arithmetic.
3. Multidimensional arrays with variable upper/lower bounds.
4. An advanced preprocessor.
5. Support true 'offsets' in addition to conventional 'pointers'.
6. Supported nested procedures.
7. Support exceptions/conditions
8. Support 'builtin' and 'intrinsic' as fundamental language concepts.
9. Abstraction of interop - use a declarative means of interfacing to code written by other languages.
10. Support 'bit' as a data type.
11. Support flexible alignment, padding and ordering of structure/array members.
12. Support endianness operations for converting a contiguous memory block from big/little to little/big endian.
13. Support multiple 'heaps' and facilitate the freeing of all allocations within a heap by simply resetting the heap.
14. Innate support for coroutines.
15. Support decimal, octal, binary and hex numeric literals.
16. Support fixed point arithmetic in decimal/binary base.
17. Remain vigilante, aware of other old and new languages like Ada, Rust, Zig, Hare, Odin etc, where are these weak? where are they strong?
Is there anything fundamental missing from this list ? (I recognize that some want to talk about threading, synchronization and multiple cores, these are important but perhaps best set aside for now, to be revisited later).
It would be interesting to also examine various examples of C based solutions to certain MCU problems to see how a new language could improve the implementation of that code.
I'd like to also remind some here that all/any of these topics are up for discussion, I am not only concerned with grammar, if anyone wants to discuss any of these aspects then please be my guest, lets explore and move forward...
1. Yes. See my previous post (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4581952/#msg4581952) in this discussion.
2. Yes.
3. Partial. Multidimensional arrays, but fixed lower bounds of zero.
4. No.
5. Absolute addressing is implemented using builtin functions ADR, GET, and PUT.
6. Yes.
7. Yes.
8. No.
9. No.
10. The datatype SET allows bit manipulation as described in Niklaus Wirth's 2007 paper:
SET: A neglected data type, and its compilation for the ARM (https://people.inf.ethz.ch/wirth/Oberon/SETs.pdf)
11. No. Unpacking / packing of structures where alignment is an issue is facilitated by builtin bitfield insert and extract inline functions.
12. No.
13. Single heap with customisable allocation / deallocation.
14. Not a language feature (unlike Oberon's predecessor Modula-2)
15. Decimal, hex and char numeric literals.
16. IEEE Floating point only.
I have been promoting and supporting the use of Oberon-07 as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe.
Frankly I think you have to write some real-life complex applications for the industry before you can say a single word with rational pearl of wisdom.
What have you seen written in Oberon?
I have been promoting and supporting the use of Oberon-07 as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe.
Frankly I think you have to write some real-life complex applications for the industry before you can say a single word with rational pearl of wisdom.
What have you seen written in Oberon?
Here's a condensed list of important goals for a new language suitable for writing code for MCUs:I have been promoting and supporting the use of Oberon-07 (https://people.inf.ethz.ch/wirth/Oberon/Oberon07.Report.pdf) as a language for programming ARM microcontrollers since 2008. The compiler and IDE is called Astrobe. I have checked it against your list of required features as follows:
1. Extensible grammar (new keywords easily added over time, 100% backward compatibility guaranteed)
2. Elimination of pointer arithmetic.
3. Multidimensional arrays with variable upper/lower bounds.
4. An advanced preprocessor.
5. Support true 'offsets' in addition to conventional 'pointers'.
6. Supported nested procedures.
7. Support exceptions/conditions
8. Support 'builtin' and 'intrinsic' as fundamental language concepts.
9. Abstraction of interop - use a declarative means of interfacing to code written by other languages.
10. Support 'bit' as a data type.
11. Support flexible alignment, padding and ordering of structure/array members.
12. Support endianness operations for converting a contiguous memory block from big/little to little/big endian.
13. Support multiple 'heaps' and facilitate the freeing of all allocations within a heap by simply resetting the heap.
14. Innate support for coroutines.
15. Support decimal, octal, binary and hex numeric literals.
16. Support fixed point arithmetic in decimal/binary base.
17. Remain vigilante, aware of other old and new languages like Ada, Rust, Zig, Hare, Odin etc, where are these weak? where are they strong?
Is there anything fundamental missing from this list ? (I recognize that some want to talk about threading, synchronization and multiple cores, these are important but perhaps best set aside for now, to be revisited later).
It would be interesting to also examine various examples of C based solutions to certain MCU problems to see how a new language could improve the implementation of that code.
I'd like to also remind some here that all/any of these topics are up for discussion, I am not only concerned with grammar, if anyone wants to discuss any of these aspects then please be my guest, lets explore and move forward...
1. Yes. See my previous post (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4581952/#msg4581952) in this discussion.
2. Yes.
3. Partial. Multidimensional arrays, but fixed lower bounds of zero.
4. No.
5. Absolute addressing is implemented using builtin functions ADR, GET, and PUT.
6. Yes.
7. Yes.
8. No.
9. No.
10. The datatype SET allows bit manipulation as described in Niklaus Wirth's 2007 paper:
SET: A neglected data type, and its compilation for the ARM (https://people.inf.ethz.ch/wirth/Oberon/SETs.pdf)
11. No. Unpacking / packing of structures where alignment is an issue is facilitated by builtin bitfield insert and extract inline functions.
12. No.
13. Single heap with customisable allocation / deallocation.
14. Not a language feature (unlike Oberon's predecessor Modula-2)
15. Decimal, hex and char numeric literals.
16. IEEE Floating point only.
Frankly I think you have to write some real-life complex applications for the industry before you can say a single word with rational pearl of wisdom.I am continually surprised by what some of our Astrobe users are managing to do with Oberon. Here are a couple of comments:
What have you seen written in Oberon?
Making good progress with the EFM32 parts. I've started using the internal EFM32 DMA controllers to manage up to 4 streams of parallel data at 2 mbit / sec plus all the other card io that goes on.
The GD32F130s are working out well for the smaller simple modules where we use many at a time ( Could be anywhere from 50 to 500 or more in a console ). The GD32F130s all have 485 data ports. A custom bootloader can update run-time firmware using the 485 data ports ( @ 2 mbit / sec ). Program update data sent from the EFM32 controllers. Can update similar type modules in parallel that way, so don't have to update them one at a time. The bootloader is installed during initial card test using a Segger & Tag-Connect footprint on the card. The bootloader is written in Oberon as well. Bootloader is under 16K, so leaves 48K for application which is lots for those modules.
Our systems are now installed at six nursing homes. This Thursday, we will install a system at a home for severe mental disability patient. Four more nursing homes want a pilot installation. It seems we have struck a nerve, especially with the younger nurses, who really embrace the support that this system is providing them.
I was reading this doument: https://www.astrobe.com/OberonGuide.pdf (https://www.astrobe.com/OberonGuide.pdf)Correct. Some time ago Elektor Magazine published an article I wrote titled: Easy Sceptre Programming with Oberon-07. The Sceptre was an ARM LPC2148 microcontroller development board designed by Elektor staff. I described one use of SETs there:
In there it defines SET to be "The sets of integers between 0 and 31".
What does this actually mean? does it mean that: 0100 0000 0000 0000 0010 1011 1101 0101 implements a set that contains the integers {0,2,4,6,7,8,9,11,13,30}?
(assuming the LSB is the rightmost bit).
Frankly I think you have to write some real-life complex applications for the industry before you can say a single word with rational pearl of wisdom.I am continually surprised by what some of our Astrobe users are managing to do with Oberon.
What have you seen written in Oberon?
The SET data type: testing, setting and clearing bits
Much of the low-level programming of the Sceptre’s LPC2148 involves testing, setting and clearing specific bits of the memory-mapped registers. This is where Oberon’s built-in SET data type is very useful.
For example, if you have LEDs connected to pins P0.13, P0.14 and P0.15 you can light them the traditional way using hexadecimal notation:
SYSTEM.PUT (LPC.IOSET, 0E000H)
Alternatively, if you prefer, using a SET constant you can say:
SYSTEM.PUT (LPC.IOSET, { 13..15 })
That is rather neat. It beats ( 1<<13 | 1<<14 | 1<<15 ) for conciseness, and even more readable as long as one recognises the curly braces as bit numbers. I assume the use of SET to define bits and the use of SET in IOSET is just coincidental. I could imagine a different architecture with LPC.IOCLR, { 13..15 } or similarExactly. IOSET is the name used in the LPC2xxx microcontroller manual. The Blinker example module has a constant declaration:
CONST
(* led(s) connected to pin P0.13, P0.14 and P0.15 *)
ledBits = {13..15};
... and an infinite main loop: WHILE TRUE DO
SYSTEM.PUT(LPC.IOCLR, ledBits);
Timer.MSecDelay(500);
SYSTEM.PUT(LPC.IOSET, ledBits);
Timer.MSecDelay(500)
END
IOCLR and IOSET are absolute memory addresses declared as constants in a separate, imported, LPC module: IOSET* = 0E0028004H;
IODIR* = 0E0028008H;
IOCLR* = 0E002800CH;
The asterisk indicates that the values are exported from the module.Frankly I think you have to write some real-life complex applications for the industry before you can say a single word with rational pearl of wisdom.I am continually surprised by what some of our Astrobe users are managing to do with Oberon.
What have you seen written in Oberon?
As Wirth said, complex systems should be designed with simple tools. He also added that he was aware of his opinion not being very popular.
Some time ago Elektor Magazine published an article I wrote titled: Easy Sceptre Programming with Oberon-07. The Sceptre was an ARM LPC2148 microcontroller development board designed by Elektor staff.
That is rather neat. It beats ( 1<<13 | 1<<14 | 1<<15 ) for conciseness
Some time ago Elektor Magazine published an article I wrote titled: Easy Sceptre Programming with Oberon-07. The Sceptre was an ARM LPC2148 microcontroller development board designed by Elektor staff.
is it possible, somehow, to get a copy?
That is rather neat. It beats ( 1<<13 | 1<<14 | 1<<15 ) for conciseness, and even more readable as long as one recognises the curly braces as bit numbers. I assume the use of SET to define bits and the use of SET in IOSET is just coincidental. I could imagine a different architecture with LPC.IOCLR, { 13..15 } or similarExactly. IOSET is the name used in the LPC2xxx microcontroller manual. The Blinker example module has a constant declaration:Code: [Select]CONST
... and an infinite main loop:
(* led(s) connected to pin P0.13, P0.14 and P0.15 *)
ledBits = {13..15};Code: [Select]WHILE TRUE DO
IOCLR and IOSET are absolute memory addresses declared as constants in a separate, imported, LPC module:
SYSTEM.PUT(LPC.IOCLR, ledBits);
Timer.MSecDelay(500);
SYSTEM.PUT(LPC.IOSET, ledBits);
Timer.MSecDelay(500)
ENDCode: [Select]IOSET* = 0E0028004H;
The asterisk indicates that the values are exported from the module.
IODIR* = 0E0028008H;
IOCLR* = 0E002800CH;
dcl interrupt_control bit(45); // usual PL/I syntax
interrupt_control = {}; // set all bits OFF
interrupt_control = {0..4,19..30}; // Wirthian notation
flags = {0..5, 7..9};
flags = flags - {3};
dcl control_reg bit(32);
control_reg = get_reg(X);
control_reg(4) = ON;
control_reg(19) = ON;
control_reg(28) = OFF;
dcl control_reg bit(32);
control_reg = get_reg(X);
control_reg = control_reg + {4,19} - {28};
That is rather neat. It beats ( 1<<13 | 1<<14 | 1<<15 ) for conciseness
But you can write (0b111<<13), this is something I do all the time.
procédé French (X)
déclarer counter binaire(15);
si counter > 0 ensuite
appeler escaper;
autre
retour;
fin;
fin;
procedure English (X)
declare counter binary(15);
if counter > 0 then
call escaper;
else
return;
end;
end;
procedure English (X)
declare counter binary(15);
if counter > 0 then
call retour; // Call the retour module to review the map's touring data.
else
return;
end;
end;
procédé French (X)
déclarer counter binaire(15);
si counter > 0 ensuite
appeler retour; // Call the retour module to review the map's touring data.
autre
retour;
fin;
fin;
procedure English (X)
declare counter binary(15);
if counter > 0 then
call retour;
else
return;
end;
loop while (a > b)
go to place;
end;
end;
procédé French (X)
déclarer counter binaire(15);
si counter > 0 ensuite
appeler retour;
autre
retour;
fin;
boucle tandis que (a > b)
aller à place;
fin;
fin;
routine German (X)
deklarieren counter binär(15);
wenn counter > 0 dann
aufrufen retour;
anders
rückkehr;
ende;
wiederholen solang (a > b)
gehen place;
ende;
ende;
процедура Russian (X)
объявлять counter бинарный(15);
counter = FACE B034 D85F BAD7:H;
если counter > 0 затем
вызов retour;
еще
возвращаться;
конец;
повторять в то время как (a > b)
ходят в place;
конец;
конец;
Russian one reads very poorly. My head would hurt if I had to read that all day long. Russian is very bad for programming languages, since words change a lot with gender, tense, plurality.
Also, "вызов" is a noun, to be consistent it should be "вызвать" (verb). Or "возвращаться" should be "возврат". But not both at the same time. And for a proper sentence structure "затем" should be "то".
And the whole idea of programming languages with different "reserved" words is pretty bad. It completely shuts down international teamwork. People don't do this not because it is impossible, but because it is impractical and nobody wants it.
There is a huge Russian accounting system from a company called 1C. They used Russian language in their scripting system. There was so much demand that they added English support. And the software is very specific for Russian market and tax laws, so there are no users outside of Russia and may be former Soviet republics.
Also, you would have to constantly switch the layout to type it, since parts of it (like constants) are still English only. This is more keystrokes, just to get a less readable result.
In English, Algol68's case statement reads case ~ in ~ out ~ esac. In Russian, this reads выб ~ в ~ либо ~ быв.
процедура Russian (X)
объявлять counter бинарный(15);
counter = FACE B034 D85F BAD7:H;
если counter > 0 то
вызвать retour;
еще
возврат;
конец;
повторять в то время как (a > b)
ходят в place;
конец;
конец;
Also, "вызов" is a noun, to be consistent it should be "вызвать" (verb). Or "возвращаться" should be "возврат". But not both at the same time.You just flipped them. Now the first one is a verb and the second one is a noun.
And the whole idea of programming languages with different "reserved" words is pretty bad. It completely shuts down international teamwork. People don't do this not because it is impossible, but because it is impractical and nobody wants it.
I'm really baffled, is this some subtle trolling attempt or someone actually wants to create such a thing
QuoteAlso, "вызов" is a noun, to be consistent it should be "вызвать" (verb). Or "возвращаться" should be "возврат". But not both at the same time.You just flipped them. Now the first one is a verb and the second one is a noun.
Also, I just realized what "ходят в" is supposed to be goto. It is far better to use "перейти к" or "переход к". "перейти" is a verb, "переход" is a noun. So, again, use whichever ones you want to use in other places.
So this wonder of modern programming language will it have a translator (for example if a German team will pick up after a Russian team) to switch back and forth of the human languages ? Or if one has to do a small change will have to do it in Russian or Chinese.
I have to say that, initially, I didn't have great expectations, many attempt were made and abandoned, but now I'm amazed, I am so amazed about this collection of useless, anti-pattern and destructive features that I hope this will become an actual language, to be used as an "a la carte" example of instant firing/ failing exam reason.
I'm really baffled, is this some subtle trolling attempt or someone actually wants to create such a thing and considers it an improvement, I'm at a loss of words...
ALL, but ABSOLUTE ALL features that could make a programing language unusable, impossible to lint, difficult to learn and use because of "flexible" keywords, abnormal syntax that doesn't follow any existing conventions, except for failing attempts decades ago, what else should it have to make it even more cumbersome and loathsome to use, mind boggles |O ?
And this is supposed to be used for microcontrollers, good Lord, mon Dieu, boje moi, милостивый бог !!!
At this time I can only assume is a trolling attempt, magisterially executed, chapeau bas !!!
Cheers and (sincerely) good luck,
DC1MC
Computer science is a branch of mathematics, so it's as idiotic as thinking about translating mathematical symbols into *something*.
So this wonder of modern programming language will it have a translator (for example if a German team will pick up after a Russian team) to switch back and forth of the human languages ? Or if one has to do a small change will have to do it in Russian or Chinese.
"to announce something clearly, firmly, publicly, or officially"
"showing little change"
declare counter binary static private;
And the whole idea of programming languages with different "reserved" words is pretty bad. It completely shuts down international teamwork. People don't do this not because it is impossible, but because it is impractical and nobody wants it.
I tend to agree.
How many languages are there? How many won't be supported?
In many of my jobs it has been normal to have several different nationalities in the team.
If multiple languages are allowed, will if( aRectangle.সূচনাকরুন() ) {...} be allowed? If not, why not?
Which language would be stored in the source code control system?
How would "diffs" be done?
Who will write the tool that allows reconciles code in Basque, Finnish, Malayalam, Arabic, Hebrew?
While we don't have any boustrophedonic languages any more, will the top-to-bottom languages be excluded?
And, of course there is the issue of pair programming? Which language wins out there?
How will IDEs cope when parsing code so they can provide ctrl-space style autocompletion?
Can a programmer add a method in Chinese to a class in Spanish with a superclass in Welsh?
Yup it is a whole can of worms. Makes APL's somewhat arbitrary symbols look like a sensible option.
Maybe there's a reason the Algol experience hasn't been repeated!
The vocabulary rendered on screen is as immaterial as the colors or fonts one uses in their IDE, I don't care if you use bold purple for C keywords or italic blue, why would I care that you have that freedom? if you find one makes your more productive, improves your concentration then use it!
If you truly want to talk about programming languages then all these issues are part of the subject, so get used to it.
And the whole idea of programming languages with different "reserved" words is pretty bad. It completely shuts down international teamwork. People don't do this not because it is impossible, but because it is impractical and nobody wants it.
I tend to agree.
How many languages are there? How many won't be supported?
In many of my jobs it has been normal to have several different nationalities in the team.
If multiple languages are allowed, will if( aRectangle.সূচনাকরুন() ) {...} be allowed? If not, why not?
Which language would be stored in the source code control system?
How would "diffs" be done?
Who will write the tool that allows reconciles code in Basque, Finnish, Malayalam, Arabic, Hebrew?
While we don't have any boustrophedonic languages any more, will the top-to-bottom languages be excluded?
And, of course there is the issue of pair programming? Which language wins out there?
How will IDEs cope when parsing code so they can provide ctrl-space style autocompletion?
Can a programmer add a method in Chinese to a class in Spanish with a superclass in Welsh?
Yup it is a whole can of worms. Makes APL's somewhat arbitrary symbols look like a sensible option.
Maybe there's a reason the Algol experience hasn't been repeated!
APL's symbols weren't arbitrary read Iverson's Turing Award lecture, hopefully you'll learn something about this subject. Your confused questions make it clear you understand little about this subject.
Everything I've said about this feature is OPTIONAL just like any number of coding standards or conventions. There is no computational reason for language keywords to be restricted to English, the abstract concepts are not English, or Japanese or Hebrew, they are universal concepts just like mathematical ideas, or APL ideas.
You can use Google translate right now to translate Russian or Norwegian articles into English, is that a bad thing? does that cause nothing but problems and confusion? do people try to mix multiple languages within the same article? think about what I'm saying to you.
Being able to translate source code from English to Spanish to Dutch to Russian with absolutely no impact on the code's meaning or runtime behavior is actually a hugely powerful capability, one you clearly cannot perceive or grasp, it goes right over your head.
And this is supposed to be used for microcontrollers
And this is supposed to be used for microcontrollersDon't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
That's me. The language is called assembler. Well, maybe not me as I don't consider myself a professional. But I do write cycle-accurate code (get the PIC to accurately detect a pulse arrival to the nearest 25ns while handling 9600 baud input in a soft UART and dealing with a 40kHz timer interrupt and a bit of I/O on the real UART on an 8-bit PIC without dropping the ball, we can do that).And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
I smelled something when "THERE SHOULD BE NO RESERVED WORDS" became "Not only there will be reserved words, but they will be translatable"
Those wishing to seriously discuss programming languages should at least make an effort to understand the difference between grammar and syntax. Its like trying to explain a passive tuned circuit to a class who don't know what DC resistance is.
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
I smelled something when "THERE SHOULD BE NO RESERVED WORDS" became "Not only there will be reserved words, but they will be translatable"
Despite replying to my post, he omitted to address the issue of how that would interact with tooling like IDEs and repositories.
Nor did he have anything to say about whether the translatable-non-keywords concept would or wouldn't be extemded to, say, if( aRectangle.সূচনাকরুন() ) {...} ? If not, why not?
And then there's concentrating on syntax, while resolutely avoiding the more important topic of what "f.g(h)" might actually mean/do in his language :)
Those wishing to seriously discuss programming languages should at least make an effort to understand the difference between grammar and syntax. Its like trying to explain a passive tuned circuit to a class who don't know what DC resistance is.
Ditto syntax and semantics - and especially their relative importance to the usefulness of the tool.
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
I smelled something when "THERE SHOULD BE NO RESERVED WORDS" became "Not only there will be reserved words, but they will be translatable"
Despite replying to my post, he omitted to address the issue of how that would interact with tooling like IDEs and repositories.
Nor did he have anything to say about whether the translatable-non-keywords concept would or wouldn't be extemded to, say, if( aRectangle.সূচনাকরুন() ) {...} ? If not, why not?
And then there's concentrating on syntax, while resolutely avoiding the more important topic of what "f.g(h)" might actually mean/do in his language :)
It is you who is fixated on syntax, I rarely use the term here myself. There are many inaccuracies in your posts here, for example one can already, today write identifiers using Eastern characters, many programming languages do this, its been around for decades, nothing new.
Those wishing to seriously discuss programming languages should at least make an effort to understand the difference between grammar and syntax. Its like trying to explain a passive tuned circuit to a class who don't know what DC resistance is.
Ditto syntax and semantics - and especially their relative importance to the usefulness of the tool.
Are they the same thing?
The vocabulary rendered on screen is as immaterial as the colors or fonts one uses in their IDE, I don't care if you use bold purple for C keywords or italic blue, why would I care that you have that freedom? if you find one makes your more productive, improves your concentration then use it!
Yes indeed. So why are you concentrating your attention on something that is "immaterial"?
QuoteIf you truly want to talk about programming languages then all these issues are part of the subject, so get used to it.
Programming languages are more about semantics and behaviour than they are about syntax. If you don't believe that, then consider the many very different things that "f.g(h)" can cause to happen in different languages.
So why are you concentrating your attention on syntax?
And the whole idea of programming languages with different "reserved" words is pretty bad. It completely shuts down international teamwork. People don't do this not because it is impossible, but because it is impractical and nobody wants it.
I tend to agree.
How many languages are there? How many won't be supported?
In many of my jobs it has been normal to have several different nationalities in the team.
If multiple languages are allowed, will if( aRectangle.সূচনাকরুন() ) {...} be allowed? If not, why not?
Which language would be stored in the source code control system?
How would "diffs" be done?
Who will write the tool that allows reconciles code in Basque, Finnish, Malayalam, Arabic, Hebrew?
While we don't have any boustrophedonic languages any more, will the top-to-bottom languages be excluded?
And, of course there is the issue of pair programming? Which language wins out there?
How will IDEs cope when parsing code so they can provide ctrl-space style autocompletion?
Can a programmer add a method in Chinese to a class in Spanish with a superclass in Welsh?
Yup it is a whole can of worms. Makes APL's somewhat arbitrary symbols look like a sensible option.
Maybe there's a reason the Algol experience hasn't been repeated!
APL's symbols weren't arbitrary read Iverson's Turing Award lecture, hopefully you'll learn something about this subject. Your confused questions make it clear you understand little about this subject.
Hence my use of the word "somewhat".
Nonetheless, to an extent all words in all languages are arbitrary. There is no reason why one specific group of animals should be called "cat" and another "dog" - and in many languages they aren't!QuoteEverything I've said about this feature is OPTIONAL just like any number of coding standards or conventions. There is no computational reason for language keywords to be restricted to English, the abstract concepts are not English, or Japanese or Hebrew, they are universal concepts just like mathematical ideas, or APL ideas.
Oh! You actually realise the points I made! From your arguments I didn't guess that.
Optionality is a problem. With C++ (and to a lesser extent with C) there are so many ways of doing the same thing, which leads to the requirement that one of many subsets of the language is chosen in each project/company.
Unjustified complexity is a problem. Even the C++ committee designing the language didn't understand the consequences of the language features' interaction; in one case they had to have their noses rubbed in it!
QuoteYou can use Google translate right now to translate Russian or Norwegian articles into English, is that a bad thing? does that cause nothing but problems and confusion? do people try to mix multiple languages within the same article? think about what I'm saying to you.
Being able to translate source code from English to Spanish to Dutch to Russian with absolutely no impact on the code's meaning or runtime behavior is actually a hugely powerful capability, one you clearly cannot perceive or grasp, it goes right over your head.
Next you'll be suggesting that ChatGPT produces output where it is guaranteed that the details are reliable.
One famous book is Papadimitriou's Computational Complexity; a book that shows that theoretical computer science is practically a branch of mathematics, mostly concerned with studies of algorithms, so it also shows that even when people say "computer science" and they usually include many things which would not be considered mathematics, for instance computer architecture, specific programming languages, etc, well, certain aspects of both "sets" are shared { logic, proofs, ...}
So computer science is practically a branch of mathematics :D
Instead of flat text files, the source code of this language would be better stored as a token stream, alongside with token-to-text mappings in each language for that project, say in a zip archive like Microsoft Office OOXML files.
Source code editors would handle lexical analysis, with the compiler, JIT, or interpreter processing the token stream instead.
Each token could be typographical (say, "Newline", "Indentation of one level", "Indentation of two levels"), syntactical (say, beginning of a quoted string, end of a quoted string, object member reference), operator (addition, subtraction, negation, multiplication, division, assignment, equality comparison), name, language keyword, and so on. This would allow things like one developer seeing quoted strings as "Thus" whereas another would see it as «Thus» and yet another as “Thus”; assignment could be := or = or even equals, for each developer working on the same source, depending on their personal preferences (by just modifying their personal token mapping). The editor would be responsible for ensuring whatever the developer writes is unambiguously tokenized.
Literal strings themselves could be included in the token mapping, although it probably should be independent of the developer token mapping, as it could be used for runtime localization (say, multi-language user interfaces).
One option for these mappings would be to store source as its Gödel number, assuming each token (and literal string) is assigned an unique natural number.
For security, the ZIP files could be encrypted with a per-project key, a reverse public key. (That is, the public key is used to encrypt the contents, and the private key used to decrypt the contents.) Or you could use a centralized project key storage. The latter would be very interesting in the business sense, as obviously the development of both the editing environment and the toolchain requires resources, so the vendor managing the project keys would ensure licensee validity, even while letting toolchain and IDE downloads be freely available.
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
I smelled something when "THERE SHOULD BE NO RESERVED WORDS" became "Not only there will be reserved words, but they will be translatable"
Despite replying to my post, he omitted to address the issue of how that would interact with tooling like IDEs and repositories.
Nor did he have anything to say about whether the translatable-non-keywords concept would or wouldn't be extemded to, say, if( aRectangle.সূচনাকরুন() ) {...} ? If not, why not?
And then there's concentrating on syntax, while resolutely avoiding the more important topic of what "f.g(h)" might actually mean/do in his language :)
And this is supposed to be used for microcontrollers
Don't forget, it was supposed to be specifically tailored for those professionals in the industry who write cycle-accurate code for PIC microcontrollers all day long!
The hilarity of this "Sherlock Holmes" character has totally surpassed anything I expected, although the signs were clear from the start. Excellent thread to follow! :-DD
I smelled something when "THERE SHOULD BE NO RESERVED WORDS" became "Not only there will be reserved words, but they will be translatable"
Despite replying to my post, he omitted to address the issue of how that would interact with tooling like IDEs and repositories.
Nor did he have anything to say about whether the translatable-non-keywords concept would or wouldn't be extemded to, say, if( aRectangle.সূচনাকরুন() ) {...} ? If not, why not?
And then there's concentrating on syntax, while resolutely avoiding the more important topic of what "f.g(h)" might actually mean/do in his language :)
It is you who is fixated on syntax, I rarely use the term here myself. There are many inaccuracies in your posts here, for example one can already, today write identifiers using Eastern characters, many programming languages do this, its been around for decades, nothing new.
And still you fail to address important considerations that don't mesh with your chosen path and viewpoint!
Those wishing to seriously discuss programming languages should at least make an effort to understand the difference between grammar and syntax. Its like trying to explain a passive tuned circuit to a class who don't know what DC resistance is.
Ditto syntax and semantics - and especially their relative importance to the usefulness of the tool.
Are they the same thing?
That question is revealing. But probably not in the way you intend!
Instead of flat text files, the source code of this language would be better stored as a token stream, alongside with token-to-text mappings in each language for that project, say in a zip archive like Microsoft Office OOXML files.
Source code editors would handle lexical analysis, with the compiler, JIT, or interpreter processing the token stream instead.
Each token could be typographical (say, "Newline", "Indentation of one level", "Indentation of two levels"), syntactical (say, beginning of a quoted string, end of a quoted string, object member reference), operator (addition, subtraction, negation, multiplication, division, assignment, equality comparison), name, language keyword, and so on. This would allow things like one developer seeing quoted strings as "Thus" whereas another would see it as «Thus» and yet another as “Thus”; assignment could be := or = or even equals, for each developer working on the same source, depending on their personal preferences (by just modifying their personal token mapping). The editor would be responsible for ensuring whatever the developer writes is unambiguously tokenized.
Literal strings themselves could be included in the token mapping, although it probably should be independent of the developer token mapping, as it could be used for runtime localization (say, multi-language user interfaces).
One option for these mappings would be to store source as its Gödel number, assuming each token (and literal string) is assigned an unique natural number.
For security, the ZIP files could be encrypted with a per-project key, a reverse public key. (That is, the public key is used to encrypt the contents, and the private key used to decrypt the contents.) Or you could use a centralized project key storage. The latter would be very interesting in the business sense, as obviously the development of both the editing environment and the toolchain requires resources, so the vendor managing the project keys would ensure licensee validity, even while letting toolchain and IDE downloads be freely available.
This is quite true, this has crossed my mind too. But then we would not be able to peruse raw text files, we'd need tooling to replace the abstract token codes with some real human vocabulary.
Instead of flat text files, the source code of this language would be better stored as a token stream, alongside with token-to-text mappings in each language for that project, say in a zip archive like Microsoft Office OOXML files.
Source code editors would handle lexical analysis, with the compiler, JIT, or interpreter processing the token stream instead.
Each token could be typographical (say, "Newline", "Indentation of one level", "Indentation of two levels"), syntactical (say, beginning of a quoted string, end of a quoted string, object member reference), operator (addition, subtraction, negation, multiplication, division, assignment, equality comparison), name, language keyword, and so on. This would allow things like one developer seeing quoted strings as "Thus" whereas another would see it as «Thus» and yet another as “Thus”; assignment could be := or = or even equals, for each developer working on the same source, depending on their personal preferences (by just modifying their personal token mapping). The editor would be responsible for ensuring whatever the developer writes is unambiguously tokenized.
Literal strings themselves could be included in the token mapping, although it probably should be independent of the developer token mapping, as it could be used for runtime localization (say, multi-language user interfaces).
One option for these mappings would be to store source as its Gödel number, assuming each token (and literal string) is assigned an unique natural number.
For security, the ZIP files could be encrypted with a per-project key, a reverse public key. (That is, the public key is used to encrypt the contents, and the private key used to decrypt the contents.) Or you could use a centralized project key storage. The latter would be very interesting in the business sense, as obviously the development of both the editing environment and the toolchain requires resources, so the vendor managing the project keys would ensure licensee validity, even while letting toolchain and IDE downloads be freely available.
This is quite true, this has crossed my mind too. But then we would not be able to peruse raw text files, we'd need tooling to replace the abstract token codes with some real human vocabulary.
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
I can't honestly tell if you are being serious right now.
To me that sounds like a nightmare. Simple syntax, having to use a specific language in a project instead of each one their own is a compromize we all have to make to share a common base. And that's good. What's good engineering if not the best possible compromize?
My PC has some settings in italian locale, other in english locale. Some software decide to use one, other software the other, without consistency. And there were cases in which the locale were mixed, who knows why.
Since you bring up excel, I also have had (very few) documents that refued to work because some functions wouldn't be translated from one language to the other. In OO Calc all menus are in english because system language is english but i have to use italian formulae. Go figure.
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
I can't honestly tell if you are being serious right now.
To me that sounds like a nightmare. Simple syntax, having to use a specific language in a project instead of each one their own is a compromize we all have to make to share a common base. And that's good. What's good engineering if not the best possible compromize?
I don't see any advantage in a programming language that recognizes only English keywords over one that can recognize English and - optionally - other cultures' keywords too. What advantage does such a programming language offer and to whom? what can the former do that the latter cannot?
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
I can't honestly tell if you are being serious right now.
To me that sounds like a nightmare. Simple syntax, having to use a specific language in a project instead of each one their own is a compromize we all have to make to share a common base. And that's good. What's good engineering if not the best possible compromize?
I'm 99.999% sure he isn't being serious, partly because he (almost always >:D ) makes sense :)
Instead he is one of several people pointing out to the OP that, while his ideas might be cute in some way, in practice they will be a can of worms. As of a couple of posts ago, the OP appears to accept that he will also be rewriting the development tools around the language!
I don't see any advantage in a programming language that recognizes only English keywords over one that can recognize English and - optionally - other cultures' keywords too. What advantage does such a programming language offer and to whom? what can the former do that the latter cannot?
Most people will ask a different question, along the lines of "What is the benefit of a language that has (non-) keywords in many languages? How does it improve the code quality and reduce cost?
In other words, don't confuse "features" with "benefits".
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
I can't honestly tell if you are being serious right now.
To me that sounds like a nightmare. Simple syntax, having to use a specific language in a project instead of each one their own is a compromize we all have to make to share a common base. And that's good. What's good engineering if not the best possible compromize?
I'm 99.999% sure he isn't being serious, partly because he (almost always >:D ) makes sense :)
Instead he is one of several people pointing out to the OP that, while his ideas might be cute in some way, in practice they will be a can of worms. As of a couple of posts ago, the OP appears to accept that he will also be rewriting the development tools around the language!
You must be paraphrasing me, can you quote the post where I said "I will also be rewriting the development tools around the language"? I said no such thing, nor would I ever express myself in such a sloppy, cavalier manner.
I don't see any advantage in a programming language that recognizes only English keywords over one that can recognize English and - optionally - other cultures' keywords too. What advantage does such a programming language offer and to whom? what can the former do that the latter cannot?
Most people will ask a different question, along the lines of "What is the benefit of a language that has (non-) keywords in many languages? How does it improve the code quality and reduce cost?
In other words, don't confuse "features" with "benefits".
Call me old fashioned but I like see questions answered in the same order they are asked, if you'd prefer to evade it then do so, but it doesn't do much for your speculative claims.
So, lets try again: What advantage does such a programming language offer and to whom? what can the former do that the latter cannot? perhaps you're at a loss for words? if so I'm forever grateful.
How else would multicultural and multilingual development teams cooperate?
Consider this the programming equivalent of personal pronouns: each subgroup gets to define how the code looks to them, without any oppressor forcing them to use a specific form. I'm quite sure this is the future of socially aware software development.
(This might also open up interesting possibilities for funding the development of such a programming language.)
I can't honestly tell if you are being serious right now.
To me that sounds like a nightmare. Simple syntax, having to use a specific language in a project instead of each one their own is a compromize we all have to make to share a common base. And that's good. What's good engineering if not the best possible compromize?
I'm 99.999% sure he isn't being serious, partly because he (almost always >:D ) makes sense :)
Instead he is one of several people pointing out to the OP that, while his ideas might be cute in some way, in practice they will be a can of worms. As of a couple of posts ago, the OP appears to accept that he will also be rewriting the development tools around the language!
You must be paraphrasing me, can you quote the post where I said "I will also be rewriting the development tools around the language"? I said no such thing, nor would I ever express myself in such a sloppy, cavalier manner.
It is implicit in https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4629565/#msg4629565 (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4629565/#msg4629565)
I don't see any advantage in a programming language that recognizes only English keywords over one that can recognize English and - optionally - other cultures' keywords too. What advantage does such a programming language offer and to whom? what can the former do that the latter cannot?
Most people will ask a different question, along the lines of "What is the benefit of a language that has (non-) keywords in many languages? How does it improve the code quality and reduce cost?
In other words, don't confuse "features" with "benefits".
Call me old fashioned but I like see questions answered in the same order they are asked, if you'd prefer to evade it then do so, but it doesn't do much for your speculative claims.
So, lets try again: What advantage does such a programming language offer and to whom? what can the former do that the latter cannot? perhaps you're at a loss for words? if so I'm forever grateful.
Call me old fashioned, but I like to skip over unimportant questions and concentrate on the "touchstone" questions, i.e. those where the answer indicates which direction to take.
Fundamentally, answering questions is relatively easy. It is more difficult (and rewarding) to find the right question to answer.
This thread is incomplete, until the new programming language name, is written in a significant number of the thousands of languages of this world.
7,117 languages:
https://www.theintrepidguide.com/how-many-languages-are-there-in-the-world/ (https://www.theintrepidguide.com/how-many-languages-are-there-in-the-world/)
Ideally, including Klingon and in pure mathematics.
The names, will be interesting in the languages which don't have a written form (if applicable).
Ideally express this list, in the form of an example program, written in your new language.
Let's say, that that program is 10,000 lines of source.
Then demonstrate how easy it is to translate that 10,000 (very approximately) line example code, into the required, 1,000 supported foreign languages.
N.B. Make sure that the program is very well documented, to put on a good show. But also, don't forget to translate all those 1,000 example programs (including all the comments! but also the variable/constant/function/procedure/etc names), into their respective native foreign language.
It can't take very long to do that task, because if it did, you might be illustrating a point (via sarcasm), that the OP may have made one or two, slight design errors in the design of this proposed new language.
For bonus points. Please state how many different keyboards were needed, to type the various symbols/characters in, and also explain how you coped, with any languages that google translate (or similar), doesn't currently handle.
Adding a keyword lexicon is a simple matter of editing a JSON file and creating a pull request, I don't care how many distinct languages they get created for, the point is they, can be, if somebody want's one they are welcome to add it.
I don't care about keyboards or input methods, anyone who uses their native alphabet today already know how to use their native alphabet I suspect. If they do not want to or find it difficult then they won't use it, if they find it easy they will use it - why is this hard for you to grasp?
You have a rare gift for making the simple appear complex, no wonder you struggle so much with some of this.
Great, since it is so simple, and my first attempt at getting you to see sense, doesn't seem to have worked...
Il était une fois dans un pays loin, loin,
Es war einmal in einem Land weit, weit entfernt,
Había una vez en un país lejano, lejano,
story = "Once upon a time in a land far, far away, there lived a little dragon with scales of red and gray. He breathed fire and flew through the skies, fierce and mighty, with his head held high."
print(story)
(setf story (concatenate 'string "Once upon a time in a land far, far away, there lived a little dragon with scales of red and gray. He breathed fire and flew through the skies, fierce and mighty, with his head held high."))
(print story)
遠くの遠い国に、昔の話があった。
赤と灰色の鱗を持った小さなドラゴンが住んでいた。
彼は火を吹いて空を飛んでいた、
強くて威風堂々と頭を高く持っていた。
C'era una volta in un paese lontano, lontano,
很久很久以前,在一个遥远的国度里
Жил-был на свете дракон маленький,
كان يوما في بلد يبعد بعيدا،
كان هناك تنين صغير ذو تشعة من الأحمر والرمادي.
وقال انه يتنفس النار ويطير من خلال السماوات،
قوي وشجاع، مع رأسه العالي
nuqneH 'ej nuqDaq vIlo'laHbe'chugh yImev
Daq ghaH Daq vIneHmeH Daqmey tIqwIj
Daq vISam Daqmey Daq vIlegh
DaqmeyDaq vIneHmeH Daq vIbe'
Havia uma vez, em um país muito, muito longe,
Det var en gång i ett land långt, långt bort,
Er was eens in een land ver, ver weg,
Estis iam en lando for, for,
Kerran, kaukana kaukana maassa,
++++++++[>++++[>++>+++>+++>+<<<<-]>+<<<<-]>++.
(=<`#9]~6ZY32Vx/4Rs+0No-&Jk)8Cj
(space) (tab) (newline) (space) (space) (tab) (newline) (tab) (tab) (newline) (tab) (space) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline) (tab) (tab) (newline)
Just in case my last post is NOT obvious enough.
Programming projects, which have got lots of different languages (spoken) in them. Can become nightmares to understand, maintain and process (for humans). Therefore, great care, should be taken, before allowing/encouraging such features, in new programming languages.
In other words. There can be extremely GOOD reasons, why existing programming languages, are the way, they are.
Just in case my last post is NOT obvious enough.
Programming projects, which have got lots of different languages (spoken) in them. Can become nightmares to understand, maintain and process (for humans). Therefore, great care, should be taken, before allowing/encouraging such features, in new programming languages.
In other words. There can be extremely GOOD reasons, why existing programming languages, are the way, they are.
Just in case my last post is NOT obvious enough.
Programming projects, which have got lots of different languages (spoken) in them. Can become nightmares to understand, maintain and process (for humans). Therefore, great care, should be taken, before allowing/encouraging such features, in new programming languages.
In other words. There can be extremely GOOD reasons, why existing programming languages, are the way, they are.
Don't forget the non-verbal non-written languages!
In this case I think one such phrase may be particularly relevant: <pheoooow, whooosh>. That can be loosely translated into English as "the sound made as the point whizzes past the OP".
Just in case my last post is NOT obvious enough.
Programming projects, which have got lots of different languages (spoken) in them. Can become nightmares to understand, maintain and process (for humans). Therefore, great care, should be taken, before allowing/encouraging such features, in new programming languages.
In other words. There can be extremely GOOD reasons, why existing programming languages, are the way, they are.
:palm:
Well at least I tried.
I think he is just trolling us with nothing but poor amount of repeated bullshit.
He does not event really want to discuss: nobody here wants that damn Babel feature, but he insists with nothing but aesthetic poor reasoning.
So, guys, give yourself a favor and give him nothing but a full ignore.
He will stop bothering us.
Allowing different language keywords will break a havoc in developing and maintaining tools which work at the source code level. Just do not do it.
I think he is just trolling us with nothing but poor amount of repeated bullshit.
He does not event really want to discuss, nobody wants that Babel feature, but he insists.
So, guys, give yourself a favor and give him nothing but a full ignore.
He will stop bothering us with his personal bullshit.
That makes sense (possible trolling). Either that, or possibly significant communications difficulties, causing them to misunderstand too much, for them to make sense of it. But there are many, possible explanations, of how this thread has come to exist.
Anyway, whatever the reason(s) is. Something funny seems to be going on, even if it is not 100% clear, what.
Also, if someone wants to invent a brand new programming language. Just go ahead and do it. Don't spend all day, creating thread(s) about it. Otherwise, it could end up going nowhere, fast.
Allowing different language keywords will break a havoc in developing and maintaining tools which work at the source code level. Just do not do it.
Why? first only a single lexicon's keywords can be used in any file, it is not permitted to ever mix lexicons in a source file. The lexicon is specified in a compiler directive or can be overridden as a compiler option. Every source file uses one and only one lexicon and the default is English, if no directive or compile option is specified.
One must "opt in" to use other lexicons.
Any source file can be converted from any lexicon to any other using a simple utility (and class library by tool builders).
Owners of repositories can establish a check that all pull requests contain source in some defined lexicon, this is trivial to do with GitHub actions, every contributor would have to save their code with that lexicon.
A developer could work locally by seeing Bulgarian keywords but always have their files saved as English.
The analogy with fonts and colors in an IDE is good, nobody cares about how the source appears to a developer, all we care about is what is shared.
Allowing different language keywords will break a havoc in developing and maintaining tools which work at the source code level. Just do not do it.
Why? first only a single lexicon's keywords can be used in any file, it is not permitted to ever mix lexicons in a source file. The lexicon is specified in a compiler directive or can be overridden as a compiler option. Every source file uses one and only one lexicon and the default is English, if no directive or compile option is specified.
One must "opt in" to use other lexicons.
Any source file can be converted from any lexicon to any other using a simple utility (and class library by tool builders).
Owners of repositories can establish a check that all pull requests contain source in some defined lexicon, this is trivial to do with GitHub actions, every contributor would have to save their code with that lexicon.
A developer could work locally by seeing Bulgarian keywords but always have their files saved as English.
The analogy with fonts and colors in an IDE is good, nobody cares about how the source appears to a developer, all we care about is what is shared.
It is your language and project, so you can do whatever you want to. I have just expressed my opinion and concern here.
procedure English (X)
declare counter binary(15);
counter = FACE B034 D85F BAD7:H;
if counter > 0 then
call retour;
else
return;
end;
loop while (a > b)
go to place;
end;
catch;
/* cannot reference container's scope, i.e. cannot reference 'counter'
end;
begin;
dcl array(x) binary(15);
// do stuff
catch;
// cannot reference 'array' but can reference 'counter'
// since the exception arose in this 'begin' block.
end;
end;
–– the code speaks for itself, after all ––
Agree with tggzzz here.
You may write external documents to explain, for instance, a very complex algorithm. But otherwise, comments in the code still stand a much better chance of being in sync and being relevant. Besides, engineers usually hate writing documents, so adding some relevant comments in the code is a better option, they usually do not hate doing this as much. Of course the comments must be relevant, so as said above, comment the why rather than the how, and the how only sparingly and only if it's really non-trivial.
Write documents for specifications though. Oh, specs, you know, the bad word. Who needs specs, right? Just write some code and see if it passes tests. :-DD
Yes, TDD is (usually) bullshit and a misnomer. Testing is good. But specs should drive development, not tests. And what tells you that you're gonna do a better job at implementing a test for some spec, than at implementing the functionality itself?
Yes, TDD is (usually) bullshit and a misnomer. Testing is good. But specs should drive development, not tests. And what tells you that you're gonna do a better job at implementing a test for some spec, than at implementing the functionality itself?
Agree with tggzzz here.
You may write external documents to explain, for instance, a very complex algorithm. But otherwise, comments in the code still stand a much better chance of being in sync and being relevant. Besides, engineers usually hate writing documents, so adding some relevant comments in the code is a better option, they usually do not hate doing this as much. Of course the comments must be relevant, so as said above, comment the why rather than the how, and the how only sparingly and only if it's really non-trivial.
; -- control volts to BCD --
; Converts the 24 bit control volt value to microvolt
; It is assumed that PWM 0x000000 = 0 Volt PWM 0xFA0000 = 5 Volt
; 0xFA0000 = 16,384,000.
; The fraction 112/367 is within 8uV at full scale, the integers
; used are limited by the 32 bit signed arithmetic routines.
; The value is not intended to be accurate, the source of the
; control voltage is unlikely to be perfect.
; Input is already pushed onto the stack
; Output is delivered to PackedD
CV2BCD:
CALL PushLit
data D'112'
CALL multiply
CALL PushLit
data D'367'
CALL divide
TI2BCD: ; entry used to convert stack to BCD
CALL Pop4
data REGA
CALL BIN2BCD
RETURN
Agree with tggzzz here.
You may write external documents to explain, for instance, a very complex algorithm. But otherwise, comments in the code still stand a much better chance of being in sync and being relevant. Besides, engineers usually hate writing documents, so adding some relevant comments in the code is a better option, they usually do not hate doing this as much. Of course the comments must be relevant, so as said above, comment the why rather than the how, and the how only sparingly and only if it's really non-trivial.
Absolutely.
'–– the code speaks for itself, after all ––' yes, it does say what it does but nothing about why. I cannot conceive why "I worked at a company once which did not permit comments". Assembler is more obtuse than most languages, I find documentation almost essential. Take the following subroutine - in any language its purpose might be divined from its name, what it is doing (multiply, divide, convert to BCD) is very clear, but why it does what it does would be very difficult to work out. In my opinion documentation is very important and any language should make it easy to do it in whatever way is convenient - blocks, one liner, etc.:Code: [Select]; -- control volts to BCD --
; Converts the 24 bit control volt value to microvolt
; It is assumed that PWM 0x000000 = 0 Volt PWM 0xFA0000 = 5 Volt
; 0xFA0000 = 16,384,000.
; The fraction 112/367 is within 8uV at full scale, the integers
; used are limited by the 32 bit signed arithmetic routines.
; The value is not intended to be accurate, the source of the
; control voltage is unlikely to be perfect.
; Input is already pushed onto the stack
; Output is delivered to PackedD
CV2BCD:
CALL PushLit
data D'112'
CALL multiply
CALL PushLit
data D'367'
CALL divide
TI2BCD: ; entry used to convert stack to BCD
CALL Pop4
data REGA
CALL BIN2BCD
RETURN
I disagree. After all, OP's purpose is to create a programming language that is concise but descriptive enough to not need comments, because users can choose the language (and perhaps even the keywords they want to use).
Organizations more or less all have their own set of rules, and whether they are good or lame, you just have to follow the rules. Discussing those rules is often pointless: if you work there, you don't have a choice, and if you don't work there, you may not have a full view of the rationale behind the rules.
The risk of comments not being up to date is one of the most common arguments I've heard for defending the zero-comment approach.
You may wonder if a developer that fails to keep comments up to date can be trusted on their code, though.
And then, as we said above, if you comment the 'why' rather than the 'how', chances of having comments not up to date are drastically lower. While changing the 'how' can happen pretty frequently (and thus, the probability of having obsolete comments is high if they are about the 'how'), the chances of changing the 'why' is much more uncommon, and if you change the 'why', then usually that means a rewrite big enough that you'd almost have to do it on purpose to keep old, obsolete comments about the 'why'. Just my 2 cents.
Finally, that's also what code reviews are for.
I personally hold the view that designing, writing, maintaining and enhancing software is best described as a "craft" rather than "science" or "engineering", I liken it to architecture myself, there are aspects that are based in physics and mechanical engineering yet there are also aspects that are aesthetics, style, atmosphere, art and so on - both aspects are essential.Spoken like a true Windows-only C# programmer who has zero idea about verifiability, robustness, or security. Congratulations!
I personally hold the view that designing, writing, maintaining and enhancing software is best described as a "craft" rather than "science" or "engineering", I liken it to architecture myself, there are aspects that are based in physics and mechanical engineering yet there are also aspects that are aesthetics, style, atmosphere, art and so on - both aspects are essential.Spoken like a true Windows-only C# programmer who has zero idea about verifiability, robustness, or security. Congratulations!
I don't recall when I last read such idiotic drivel, and I often read people spouting conspiracy theories at Rense and AboveTopSecret.
It's like listening to a bridge-builder describe how they scoff at stuff like engineering calculations, and instead prefers to call themselves a craftsman. Would you trust such a bridge? I wouldn't.
The world is already chock full of crappy software written by craftsmen and monkeys, ignoring all engineering principles and scientific thinking and research.
We do not need a single more. Please!
To repeat once again: to develop a new language, you need to consider both how the concepts and algorithms are implemented in that language, and what kind of machine code one should expect them to generate. Starting from the syntax and grammar is starting from how the code should look like; something that logically should be decided last, just before the language specification itself is finalized. You need to ask yourself "Why would I want to implement algorithm X or system Y in this language, as opposed to some other language?". If your answer is "Because it's better", you do not have the skill in design, logic, and software engineering to pull it off, sorry; and are just daydreaming about appearances without any actual content.
I had very similar thoughts, the major difference being that C# is no worse than other languages.It is similar to Java in that it compiles to CIL (https://en.wikipedia.org/wiki/Common_Intermediate_Language), a stack-based bytecode interpreter, which is touted as "portable". We all know how portable Java is, and how portable .Net/Mono is: not very, when you start trying to get actual work done, instead of simple examples. Instead of acknowledging differences between operating systems and hardware architectures, it applies a heavy (again, bytecode interpreter/JIT compiler) abstraction layer to hide them. For this reason alone, C# is not a language one should base their understanding of programming languages on.
Java ... my Java-cup says "program once, use many"...and I laugh every time I use it to drink my coffee.Exactly.
My last few posts have been an experiment. As I've mentioned in other discussions, I sometimes entertain ideas and concepts I do not believe in at all, like conspiracy theories, just to find out where they lead to, and how their proponents see the world around them. In this case, I inverted everything I have learned in practice about teamwork, portability, maintainability, and so on, and wanted to see if OP has any pushback to the known non-working ideas I would describe.Aha. I looked at some of those and thought WTF? I want to be constructive so try not to malign other posts.
I had very similar thoughts, the major difference being that C# is no worse than other languages.It is similar to Java in that it compiles to CIL (https://en.wikipedia.org/wiki/Common_Intermediate_Language), a stack-based bytecode interpreter, which is touted as "portable". We all know how portable Java is, and how portable .Net/Mono is: not very, when you start trying to get actual work done, instead of simple examples. Instead of acknowledging differences between operating systems and hardware architectures, it applies a heavy (again, bytecode interpreter/JIT compiler) abstraction layer to hide them. For this reason alone, C# is not a language one should base their understanding of programming languages on.
Did I even imply they were? I am only describing the reasons why I believe C# is not sufficient or suitable as the basis for ones understanding for designing a programming language.I had very similar thoughts, the major difference being that C# is no worse than other languages.It is similar to Java in that it compiles to CIL (https://en.wikipedia.org/wiki/Common_Intermediate_Language), a stack-based bytecode interpreter, which is touted as "portable". We all know how portable Java is, and how portable .Net/Mono is: not very, when you start trying to get actual work done, instead of simple examples. Instead of acknowledging differences between operating systems and hardware architectures, it applies a heavy (again, bytecode interpreter/JIT compiler) abstraction layer to hide them. For this reason alone, C# is not a language one should base their understanding of programming languages on.
Wanting to define the result of every construct and getting bit-identical results from every program, no matter what machine or environment it is run on, are not evil goals.
Lots of people use things much more inefficient than Java or C#.Yes, I use such things as well, but how is that supposed to be relevant here?
I had very similar thoughts, the major difference being that C# is no worse than other languages.It is similar to Java in that it compiles to CIL (https://en.wikipedia.org/wiki/Common_Intermediate_Language), a stack-based bytecode interpreter, which is touted as "portable". We all know how portable Java is, and how portable .Net/Mono is: not very, when you start trying to get actual work done, instead of simple examples. Instead of acknowledging differences between operating systems and hardware architectures, it applies a heavy (again, bytecode interpreter/JIT compiler) abstraction layer to hide them.
For this reason alone, C# is not a language one should base their understanding of programming languages on.
Portability and adaptation to different architectures, and understanding the different approaches (paradigms) different languages have, is utterly paramount for understanding software engineering in a way that one can apply in more than one particular niche (Windows C# programming). C# is designed to avoid having to do that.
If we ignore the single-vendor control, the reliance on CIL, and just look at the language specification, say the latest version (C# 6.0) standardized by ECMA in ECMA-334.pdf (https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-334.pdf) –– noting that Microsoft published it in 2015, ECMA-334 was published in 2022, and Microsoft has already published C# 11.0 –– you could compare it to older versions of C++. In particular, it does not have specific-size integers, and instead codifies that 'short' is 16-bit, 'int' is 32-bit, 'long' 64-bit, and the 'char' type uses UTF-16; i.e. IP32 or LP64.
In short, meh.
Java ... my Java-cup says "program once, use many"...and I laugh every time I use it to drink my coffee.Exactly.
If we do not learn from the past, we're doomed to repeat the same mistakes, as well.
String Literals
How important is a rich support for string literals in an MCU world? C# recently introduced an impressive innovation, is this useful? how important is this kind of feature?
https://www.stevefenton.co.uk/blog/2022/02/raw-string-literals-in-c/ (https://www.stevefenton.co.uk/blog/2022/02/raw-string-literals-in-c/)
In MCU world, not only string literals, but ability to include a binary file would be actually handy. Something like
uint8_t coeffs[] =
#binary_include "coeffs.bin"
so that one does not need to write conversion tools from binary to "{0x12, 0x34"} etc.
String Literals
How important is a rich support for string literals in an MCU world? C# recently introduced an impressive innovation, is this useful? how important is this kind of feature?
https://www.stevefenton.co.uk/blog/2022/02/raw-string-literals-in-c/ (https://www.stevefenton.co.uk/blog/2022/02/raw-string-literals-in-c/)
"Feature" is the right word. Without "benefit" nobody will care.
The end result is easily achievable right now. Hence it is syntactic sugar (https://en.wikipedia.org/wiki/Syntactic_sugar#Criticism), not a benefit.
But no doubt those that go into paroxyisms of delight that the Edge browser may have rounded corners (https://www.techradar.com/news/microsoft-edge-could-go-for-rounded-corners-in-a-big-way-but-that-might-not-be-popular) (or not) will become ecstatic.
In MCU world, not only string literals, but ability to include a binary file would be actually handy. Something like
uint8_t coeffs[] =
#binary_include "coeffs.bin"
so that one does not need to write conversion tools from binary to "{0x12, 0x34"} etc.
counter = FACE B034 D85F BAD7:H;
dcl coeffs(1024) bin(8);
coeffs = AB CE 7A 88 96 4E 3D.......:H;
dcl coeffs(1024) bin(16);
coeffs = ABCE 7A88 964E 3D42 0004.......:H;
dcl coeffs(1024) bin(16);
coeffs =
#binclude "adc_table.h"
;
In MCU world, not only string literals, but ability to include a binary file would be actually handy. Something likePretty sure that somewhere on stackoverflow there is a stupid little script which achieves this with binutils; then it should be a simple matter of linking the resulting .o into your project and
uint8_t coeffs[] =
#binary_include "coeffs.bin"
so that one does not need to write conversion tools from binary to "{0x12, 0x34"} etc.
coeffs.h:
extern uint8_t *coeffs;
extern size_t coeffs_size;
RESOURCES = \
res/icon.png \
res/image_1.png \
res/image_2.png \
LIBS += -Wl,--format=binary -Wl,--start-group $(RESOURCES) -Wl,--end-group -Wl,--format=default
extern uint8_t _binary_res_image_1_png_start;
extern uint8_t _binary_res_image_1_png_end;
uint8_t *data = &_binary_res_image_1_png_start;
int size = (int)(&_binary_res_image_1_png_end - &_binary_res_image_1_png_start);
LIBS += -Wl,--format=binary -Wl,--start-group $(RESOURCES) -Wl,--end-group -Wl,--format=default
I do that binary inclusion with GCC for some project. It works, but I can't say it is pretty. Here is what I do for that
In the command line pass the files you want to be included as raw byte arrays:Code: [Select]RESOURCES = \
res/icon.png \
res/image_1.png \
res/image_2.png \
LIBS += -Wl,--format=binary -Wl,--start-group $(RESOURCES) -Wl,--end-group -Wl,--format=default
And then in the code declare the symbols corresponding to those file:Code: [Select]extern uint8_t _binary_res_image_1_png_start;
extern uint8_t _binary_res_image_1_png_end;
Then you can get a pointer and a size as this:Code: [Select]uint8_t *data = &_binary_res_image_1_png_start;
int size = (int)(&_binary_res_image_1_png_end - &_binary_res_image_1_png_start);
It is better than using external tools, for sure. And in practice I don't find myself needing to do this a lot, so not a huge issue.
$ ld -o opa2604.o --format=binary --start-group opa2604_30v.png opa2604_48v.png --end-group
ld: warning: cannot find entry symbol _start; not setting start address
$ ls -l opa2604*
-rw-r--r-- 1 m users 45920 2019-03-27 opa2604_30v.png
-rw-r--r-- 1 m users 44319 2019-03-27 opa2604_48v.png
-rwxr-xr-x 1 m users 94956 01-14 20:33 opa2604.o
$ objdump -t opa2604.o
opa2604.o: file format elf32-i386
SYMBOL TABLE:
08049000 l d .data 00000000 .data
00000000 *UND* 00000000 _start
0805f07f g .data 00000000 __bss_start
08049000 g .data 00000000 _binary_opa2604_30v_png_start
0805f07f g .data 00000000 _binary_opa2604_48v_png_end
0805f07f g .data 00000000 _edata
0805f080 g .data 00000000 _end
08054360 g .data 00000000 _binary_opa2604_30v_png_end
0000ad1f g *ABS* 00000000 _binary_opa2604_48v_png_size
08054360 g .data 00000000 _binary_opa2604_48v_png_start
0000b360 g *ABS* 00000000 _binary_opa2604_30v_png_size
It even gives you those _size symbols, so you don't need to calculate blob sizes manually.But they are not ordinary variables and trying to use them as such causes segfaults.
So injecting binary static data in this manner is deemed sufficient? an ability have a compiler do this simply by compiling the code, wouldn't be that helpful?
So injecting binary static data in this manner is deemed sufficient? an ability have a compiler do this simply by compiling the code, wouldn't be that helpful?
It's not "injecting" data any more than a compiler injects data with compiled object code. It's the same thing.
I actually kinda like it. It's generating object files from binary files, adding this in the programming language itself doesn't add anything much, except possibly making it cuter, but this can be made cuter by wraoping it with a few lines of code (as I mentioned.)
The plus point is precisely that it's language-agnostic. Writing a wrapper for the language you use is relatively trivial.
This is pretty much what resource compilers were doing on Windows and MacOS. But this approach only requires binutils and is thus portable as long as you are using binutils.
procedure main (arg);
/* This is sample code */
dcl name string(32);
dcl I bin(15);
dcl J bin(15);
dcl S bin(31);
dcl A(1024) bit(8); // 1 KByte
dcl root ptr;
dcl status bin(15);
type_a = "string literlas can be single quoted like this";
type_b = ""or double quoted like this, with embedded single quoted text "like this" see?"";
name = ""demonstration_network_controller"" ;
title = ""This is the "best in class"
language system"" ;
//call sys$announce_device(name, status);
if status ~= 0 then
return;
end;
call sys$install_platform(title);
root = sys$get_system_bootroot(name);
call sys$validate_authority(root);
goto cycle_loop;
@cycle_loop
I = 100;
if I > 0 then
goto cycle_loop;
end;
I = sys$get_updated_count(J);
@setpoint(0)
loop while (I >= J)
I = get_updated_count(J);
end;
if I = 123 & J = 321 then
I = 0;
goto setpoint(I); // never pass non-zero !!
elif J + I > J * I then
goto cycle_loop;
end;
call get_latest_faulting_stack(S);
if S ~= 0 then
call sys$stack_crawler(S);
end;
/* set S to the sentinel for the next operation */
S = F5D3 03A2:H; // we need to ensure this sentinel is not allocated to any other device types
call sys$reinitialize_firmware_table(S);
call sys$sleep (1024);
goto cycle_loop ;
/* Crawl the stack chain looking for the designated handler */
procedure sys$stack_crawler (handler_id) recursive;
arg handler_id bin(31);
end;
/* Only call this if we kmow there's no active services still running */
procedure sys$reinitialize_firmware_table(table);
arg table bin(31);
end;
procedure sys$timer_callback (context) interrupt;
arg context pointer;
end;
end;
So injecting binary static data in this manner is deemed sufficient? an ability have a compiler do this simply by compiling the code, wouldn't be that helpful?
It's not "injecting" data any more than a compiler injects data with compiled object code. It's the same thing.
I actually kinda like it. It's generating object files from binary files, adding this in the programming language itself doesn't add anything much, except possibly making it cuter, but this can be made cuter by wraoping it with a few lines of code (as I mentioned.)
The plus point is precisely that it's language-agnostic. Writing a wrapper for the language you use is relatively trivial.
This is pretty much what resource compilers were doing on Windows and MacOS. But this approach only requires binutils and is thus portable as long as you are using binutils.
Well this is precisely the kind of thing that I want to support, its easy to implement, like the switchable keyword lexicons, I need to think about it though, step back and see the 'big picture', the preprocessor is all about executing code at compile time and I want to take a long look at preprocessing anyway.
So injecting binary static data in this manner is deemed sufficient? an ability have a compiler do this simply by compiling the code, wouldn't be that helpful?
It's not "injecting" data any more than a compiler injects data with compiled object code. It's the same thing.
I actually kinda like it. It's generating object files from binary files, adding this in the programming language itself doesn't add anything much, except possibly making it cuter, but this can be made cuter by wraoping it with a few lines of code (as I mentioned.)
The plus point is precisely that it's language-agnostic. Writing a wrapper for the language you use is relatively trivial.
This is pretty much what resource compilers were doing on Windows and MacOS. But this approach only requires binutils and is thus portable as long as you are using binutils.
Well this is precisely the kind of thing that I want to support, its easy to implement, like the switchable keyword lexicons, I need to think about it though, step back and see the 'big picture', the preprocessor is all about executing code at compile time and I want to take a long look at preprocessing anyway.
Well, the "wrapper" I was talking about uses some preprocessor to help, so if you implement a better preprocessor than the simple C one, that sure would help here.
Just be aware that writing good compile-time stuff that can fully replace a preprocessor (and do more and better) is no simple task.
@SiliconWizard
"But this approach only requires binutils "
Yup, not supported by Acorn DSE (riscOS), CodeWarrior-SONY-ps1 (Windows) and Sierra (dos).
No binutils -> you need a specific tool.
The Nintendo sdk for GB and GBA come with a tool to convert a gif image into a c file stuffing an array with hex values.
The Acorn DSE does the same.
Pure garbage. Again.
Pure garbage. Again.
Perhaps programming language design isn't a good fit for you, you feel inadequate, unable to contribute meaningfully, so you lash out, your emotions take over.
You might do better with flower arranging or something less intellectually strenuous, a man must know his limitation after all...
@guys, ignore this troll, seriously, and he will leave the forum.
Otherwise we have to babysit this grown dude.
I am seriously going to ignore the whole topic, I have seriously enough.
I've drafted a document that compares language features of C, PL/I and the hypothetical IPL, where the features are conducive to writing an operating system in that language, for those interested jut visit the page in GitHub (https://github.com/Steadsoft/imperium/blob/main/docs/README.md).
This arose from discussions elsewhere. The impetus was my view that a language good for MCU work must also be suitable for developing an operating system, any language claiming to be "good" at hardware programming must also be good for OS development.
To my knowledge few language have a track record of delivering real operating systems, PL/I and C are the most well known I think.
To my knowledge few language have a track record of delivering real operating systemsHere are some such languages I can think of:
I've drafted a document that compares language features of C, PL/I and the hypothetical IPL, where the features are conducive to writing an operating system in that language, for those interested jut visit the page in GitHub (https://github.com/Steadsoft/imperium/blob/main/docs/README.md).
This arose from discussions elsewhere. The impetus was my view that a language good for MCU work must also be suitable for developing an operating system, any language claiming to be "good" at hardware programming must also be good for OS development.
To my knowledge few language have a track record of delivering real operating systems, PL/I and C are the most well known I think.
<Waffle, Waffle, Waffle>
You would benefit from having a wider experience and less of a tunnel vision.
To my knowledge few language have a track record of delivering real operating systemsHere are some such languages I can think of:
Pascal:
UCSD Pascal and CP/M-68K
Modula-2:
Acorn Archimedes and ETH Medos-2
Oberon:
Project Oberon, ETHOS, AOS/A2
Also read the article in The Register: The wild world of non-C operating systems
https://www.theregister.com/2022/03/29/non_c_operating_systems/ (https://www.theregister.com/2022/03/29/non_c_operating_systems/)
My core thesis here though is that if a language is ill suited to writing an OS...
My core thesis here though is that if a language is ill suited to writing an OS then it's ill suited for use as an MCU language,
My core thesis here though is that if a language is ill suited to writing an OS...
In a completely abstract, machine-independent way?
With or without a certain amount of additional assembly code?
My core thesis here though is that if a language is ill suited to writing an OS then it's ill suited for use as an MCU language,
Well, I think almost everyone says that, even these days, although it's usually defined in the positive form rather than the negative one that you used here.
It's what is commonly called a "system programming language". A reasonable list is there:
https://en.wikipedia.org/wiki/System_programming_language
It's not exhaustive though by any means. Just regarding Wirth languages, they don't even list Oberon, while it's been designed (and used) to develop a full OS, and it's being used as a language for programming MCUs (which is the whole business of one of the forum users here.)
My core thesis here though is that if a language is ill suited to writing an OS then it's ill suited for use as an MCU languageI dunno. Any language can write an OS, given appropriate "support functions" written in some other language.
QuoteMy core thesis here though is that if a language is ill suited to writing an OS then it's ill suited for use as an MCU languageI dunno. Any language can write an OS, given appropriate "support functions" written in some other language.
Are you really counting early CP/M and MSDOS 1 as "operating systems"? That's a pretty low bar.
BTW, CP/M 2.2 (which was what was heavily used) appears to have been re-written in assembly language. http://www.cpm.z80.de/source.html (http://www.cpm.z80.de/source.html)
What about "self-hosting"? Is it important for your language to be able to compile itself? This seems to be an implicit requirement for modern languages, but I'm not really sure it should be.
Finally, I'm not sure what the hangup is about avoiding assembly language is, in the first place. By the time you talk about some of the features that have come up ("equal cycle length code paths"), I think you've already thrown portability - a major selling point of HLLs - out the door...
Code: [Select]LIBS += -Wl,--format=binary -Wl,--start-group $(RESOURCES) -Wl,--end-group -Wl,--format=default
> avr-objcopy -O elf32-avr -I binary binfile foo.elf
> avr-objdump -x foo.elf
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 0000000b 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
00000000 l d .data 00000000 .data
00000000 g .data 00000000 _binary_binfile_start
0000000b g .data 00000000 _binary_binfile_end
0000000b g *ABS* 00000000 _binary_binfile_size
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
It persists because it is still dangerous. Many languages try to avoid the danger and impose rules that are 95% helpful and 5% frustrating.
In my opinion.
Well, pointers were there in languages that influenced the design of C.I was not intending to imply C was the first language to use pointers. What I was trying to say is the confluence of pointers, all the logical operators, and a standard library that has useful system oriented functions gives C the right ingredients to be a systems programming language. Which was what OP asked.
Well, pointers were there in languages that influenced the design of C.I was not intending to imply C was the first language to use pointers. What I was trying to say is the confluence of pointers, all the logical operators, and a standard library that has useful system oriented functions gives C the right ingredients to be a systems programming language. Which was what OP asked.
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
It persists because it is still dangerous. Many languages try to avoid the danger and impose rules that are 95% helpful and 5% frustrating.
In my opinion.
Well, pointers were there in languages that influenced the design of C.
To take a less well known example, consider Algol68. INT x states x is an immutable integer, REF INT x states x is a reference to an integer which is variable, REF REF INT state x is a pointer to an integer. In Algol 68 the types [10]INT, [10]REF INT, REF[10]INT, REF[10]REF INT are all distinct types and all useful. They closely correspond to the types declared by these C declarations:
typedef const int i[10]; typedef const int *ir[10]; typedef int const *ri[10]; typedef int const *rir[10]; http://www.cap-lore.com/Languages/aref.html (http://www.cap-lore.com/Languages/aref.html)
I wonder if the OP realises that Algol68 has many of the features he deems desirable, e.g. "if" can be a variable, and it has symbol set (with alternatives) that work on the various different character sets available on computers at the time. And those are just the start. https://opensource.com/article/20/6/algol68 (https://opensource.com/article/20/6/algol68) Truly, Algol is an improvement on most of its successors.
I suspect the OP is unaware of https://rosettacode.org/wiki/Rosetta_Code (https://rosettacode.org/wiki/Rosetta_Code) which gives the same program in many different languages. Examples: there are 68 implementations of Dijkstra's algorithms, and bitwise operations implemented in 144 languages.
That really ought to give him something to "compare and contrast" before committing to something for his language.
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
It persists because it is still dangerous. Many languages try to avoid the danger and impose rules that are 95% helpful and 5% frustrating.
In my opinion.
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
It persists because it is still dangerous. Many languages try to avoid the danger and impose rules that are 95% helpful and 5% frustrating.
In my opinion.
Well, pointers were there in languages that influenced the design of C.
To take a less well known example, consider Algol68. INT x states x is an immutable integer, REF INT x states x is a reference to an integer which is variable, REF REF INT state x is a pointer to an integer. In Algol 68 the types [10]INT, [10]REF INT, REF[10]INT, REF[10]REF INT are all distinct types and all useful. They closely correspond to the types declared by these C declarations:
typedef const int i[10]; typedef const int *ir[10]; typedef int const *ri[10]; typedef int const *rir[10]; http://www.cap-lore.com/Languages/aref.html (http://www.cap-lore.com/Languages/aref.html)
I wonder if the OP realises that Algol68 has many of the features he deems desirable, e.g. "if" can be a variable, and it has symbol set (with alternatives) that work on the various different character sets available on computers at the time. And those are just the start. https://opensource.com/article/20/6/algol68 (https://opensource.com/article/20/6/algol68) Truly, Algol is an improvement on most of its successors.
I suspect the OP is unaware of https://rosettacode.org/wiki/Rosetta_Code (https://rosettacode.org/wiki/Rosetta_Code) which gives the same program in many different languages. Examples: there are 68 implementations of Dijkstra's algorithms, and bitwise operations implemented in 144 languages.
That really ought to give him something to "compare and contrast" before committing to something for his language.
Algol68 was the language that most influenced PL/I which I've spoken about at length here, there was also a build of it that accepted Russian keywords, I wonder if you were aware of that.
I have to ask, politely, would you please stop insinuating that I am "unqualified" in some way or other, to discuss this subject? Repeatedly making disparaging remarks and insulting comments is really not what I expect in any forum like this.
I went to the trouble in my very first post to mention that I was experienced with compiler design and development, I anticipated that some might be skeptical, that a language is a large undertaking and not to be treated glibly, I know all that and therefore summarized what I had done before.
Yet you continue to insinuate I'm some kind of idiot.
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?I think it was the concept of a pointer *, which often resolved to a memory address, and that address could be just about anything. Fun things like **thing were simple to write (but maybe not so simple to understand). Also the inclusion of all bit operators and/or/xor/shifts. The standard library includes system type functions like malloc(). A lot of C maps well to machine instructions. And the lack of any nanny code like array bound checking made it fast (but dangerous).
It persists because it is still dangerous. Many languages try to avoid the danger and impose rules that are 95% helpful and 5% frustrating.
In my opinion.
Well, pointers were there in languages that influenced the design of C.
To take a less well known example, consider Algol68. INT x states x is an immutable integer, REF INT x states x is a reference to an integer which is variable, REF REF INT state x is a pointer to an integer. In Algol 68 the types [10]INT, [10]REF INT, REF[10]INT, REF[10]REF INT are all distinct types and all useful. They closely correspond to the types declared by these C declarations:
typedef const int i[10]; typedef const int *ir[10]; typedef int const *ri[10]; typedef int const *rir[10]; http://www.cap-lore.com/Languages/aref.html (http://www.cap-lore.com/Languages/aref.html)
I wonder if the OP realises that Algol68 has many of the features he deems desirable, e.g. "if" can be a variable, and it has symbol set (with alternatives) that work on the various different character sets available on computers at the time. And those are just the start. https://opensource.com/article/20/6/algol68 (https://opensource.com/article/20/6/algol68) Truly, Algol is an improvement on most of its successors.
I suspect the OP is unaware of https://rosettacode.org/wiki/Rosetta_Code (https://rosettacode.org/wiki/Rosetta_Code) which gives the same program in many different languages. Examples: there are 68 implementations of Dijkstra's algorithms, and bitwise operations implemented in 144 languages.
That really ought to give him something to "compare and contrast" before committing to something for his language.
Algol68 was the language that most influenced PL/I which I've spoken about at length here, there was also a build of it that accepted Russian keywords, I wonder if you were aware of that.
I was aware of the Russian Algol68 variant thanks. PL/1 never made an impression on this side of the pond. The nearest was a brief flurry of interest in PL/M in the late 70s.
You have seemed to be interested in expressing bit operations. Which of the 144 examples are you thinking of following. Do you prefer the VHDL or Smalltalk concepts? If none of the 144, what will be in your 145th variant?
QuoteI have to ask, politely, would you please stop insinuating that I am "unqualified" in some way or other, to discuss this subject? Repeatedly making disparaging remarks and insulting comments is really not what I expect in any forum like this.
It isn't necessarily disparaging to regard someone as unqualified; we have all been unqualified. The key point is what any such person does or does not do to become more qualified. "Learning the literature/history" is necessary.
In my own professional work I have touched on a variety of different fields. I've done my work in mathematical linguistics, for example, without any professional credentials in mathematics; in this subject I am completely self-taught, and not very well taught. But I've often been invited by universities to speak on mathematical linguistics at mathematics seminars and colloquia. No one has ever asked me whether I have the appropriate credentials to speak on these subjects; the mathematicians couldn't care less. What they want to know is what I have to say. No one has ever objected to my right to speak, asking whether I have a doctor's degree in mathematics, or whether I have taken advanced courses in the subject. That would never have entered their minds. They want to know whether I am right or wrong, whether the subject is interesting or not, whether better approaches are possible—the discussion dealt with the subject, not with my right to discuss it.
QuoteI went to the trouble in my very first post to mention that I was experienced with compiler design and development, I anticipated that some might be skeptical, that a language is a large undertaking and not to be treated glibly, I know all that and therefore summarized what I had done before.
Yet you continue to insinuate I'm some kind of idiot.
Implementing a compiler and selecting which concepts to use/discard when creating a language have very little in common.
From your statements in this thread, it appears that your experience is limited to a relatively narrow subset of the conceptual types of languages in existence[1].
You fail to engage with suggestions that would widen (possibly beneficially) your understanding. For example, you've completely ignored the possibility of your language including one facility that is very useful in resource-constrained embedded systems: fixed point arithmetic.
I don't remember you discussing how multicore/multithread parallel processing will be expressed in your language; that will be critically important in the future.
I try to become less inexperienced in the things that matter to me. Knowing how and when to use languages that are fundamentally and conceptually very different is one such case. OTOH, implementing a compiler is completely uninteresting to me.
[1] e.g. C, Pascal, PL/M, Delphi, Coral-66 are all procedural languages with little difference between them. Conceptually very different languages are VHDL, LISP, Smalltalk, Forth, OCCAM/xC etc etc.
Leaving ubiquity and popularity and availability aside, what is it about C that give it any kind of status as a systems programming language?
I don't get the premise of your question nor the question itself. It looks like some users that have replied to this have taken it as "what are the features of C that make it appropriate as a system programming language", but that is not what I read in the above, that's not what you asked.
The exact answer to your exact question is obvious - C was explicitely designed to implement an OS. It doesn't need a "status", it's just what it was meant for. And history has proven a long track record of it being appropriate indeed for OS development and low-level stuff in general.
That doesn't mean it's "perfect", or that it's the only language for such endeavours. Other languages have been clearly used, as we already mentioned. A good chunk of the initial MacOS, and base applications was written in Pascal. Wirth designed Oberon as a language for implementing the Oberon OS. Ada has been extensively used for embedded development, and there are a few OSs written in Ada as well.
If the question was "what features of C are key features for a system prog language", then the answer is relatively simple. It has high-level features on par with languages such as Pascal (so not very high-level, bug high-level enough to express abstraction), but can get low-level enough (yes, in particular through the use of pointers, pointer arithmetic, logic operators, function pointers, ...) to address hardware almost directly.
Any modern language that wants to do the same usually resorts to "unsafe" blocks of code, such as Rust, but the idea of unsafe code was already there in Modula-3 - I suggested reading the language report which can be found online. The point is that once you have to use your language for system programming, you'll have to add ways of circumventing all your nice protections by adding "unsafe"constructs.
While that may look like a reasonable approach - instruct the developers to use safe code as much as possible, and restrict unsafe code to the bare minimum - the same can be achieved using multiple languages. Using a very high-level language along with assembly, or along with C for the very low-level code, also works, and is not significantly better or worse than trying to stuff all source code with a single language and resorting to tricks to achieve that.
Just my 2 cents.
Most of the C# code you write is "verifiably safe code." Verifiably safe code means .NET tools can verify that the code is safe. In general, safe code doesn't directly access memory using pointers. It also doesn't allocate raw memory. It creates managed objects instead.
Are you claiming there were no computer installations in the UK that used PL/I?Algol68 was the language that most influenced PL/I which I've spoken about at length here, there was also a build of it that accepted Russian keywords, I wonder if you were aware of that.I was aware of the Russian Algol68 variant thanks. PL/1 never made an impression on this side of the pond. The nearest was a brief flurry of interest in PL/M in the late 70s.
You have seemed to be interested in expressing bit operations. Which of the 144 examples are you thinking of following. Do you prefer the VHDL or Smalltalk concepts? If none of the 144, what will be in your 145th variant?
I'm considering symbolic operators for left/right logical shift, right arithmetic shift, rotate left and rotate right, and possibly population count, is that what you're asking me?
QuoteI have to ask, politely, would you please stop insinuating that I am "unqualified" in some way or other, to discuss this subject? Repeatedly making disparaging remarks and insulting comments is really not what I expect in any forum like this.
It isn't necessarily disparaging to regard someone as unqualified; we have all been unqualified. The key point is what any such person does or does not do to become more qualified. "Learning the literature/history" is necessary.
The key point is that you are conflating (no doubt intentionally) one's certifications with one's argument.
How did you establish that view? it seems to be an opinion and an imprecise one at that. It also now seems you don't doubt my capacity to design and build a real working compiler then...QuoteI went to the trouble in my very first post to mention that I was experienced with compiler design and development, I anticipated that some might be skeptical, that a language is a large undertaking and not to be treated glibly, I know all that and therefore summarized what I had done before.Implementing a compiler and selecting which concepts to use/discard when creating a language have very little in common.
Yet you continue to insinuate I'm some kind of idiot.
... but you doubt my ability to "select concepts" to include in a new language, I can select whatever I want to select though, I started the thread.
You fail to engage with suggestions that would widen (possibly beneficially) your understanding. For example, you've completely ignored the possibility of your language including one facility that is very useful in resource-constrained embedded systems: fixed point arithmetic.
Would you like me to show you the posts where I advocated fixed point binary and decimal types and arithmetic? perhaps you overlooked them, if I showed them to you they'd prove you wrong on this specific point wouldn't they? Seriously, just say the word and I'll show you those posts.
I don't remember you discussing how multicore/multithread parallel processing will be expressed in your language; that will be critically important in the future.
If you want to discuss that then do so, have you asked me specifically about this area and been ignored? Why do you think I should be raising things that you regard as relevant, just raise them. Berating me for not raising points that you want to raise seems frankly pathetic.
I try to become less inexperienced in the things that matter to me. Knowing how and when to use languages that are fundamentally and conceptually very different is one such case. OTOH, implementing a compiler is completely uninteresting to me.
So just to be very clear you've never personally designed and written lexical analyzers, grammars, parsers, code generators or optimizers then?
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
_Alignas _Generic _Thread_local
_Alignof _Noreturn
_Atomic _Static_assert
I’ve always been nonplussed that the average assembler has a much more capable macro language than the C preprocessor.
select (state);
when (1,4,7)
call flush_buffers(-1);
when (2,3)
call add_to_buffer(state, status);
if status > 0 then
call abort;
when (get_latest_failure(state))
call log("There was at least one failure");
else
call log("All is well");
end;
Operator symbols
Given that I want to support arithmetic/logical shift left/right along with rotate left and right, the question emerges as to what priorities these operators should be given as well as the actual symbol to use for them.
For rotation these sprang to mind:
Rotate left: <@<
Rotate right: >@>
The circularity of the @ seems a reasonable sign for rotation.
Anyway, suggestions welcome.
L_ROTATE: ('<<@'); // rotate: left bit rotated out rite bit becomes that rotated left bit
R_ROTATE: ('@>>'); // rotate: rite bit rotated out left bit becomes that rotated rite bit
This is just so stupid.
The number of times rotate is used in programs, there is absolutely no disadvantage in typing rotate_right_through_carry(x, i). There is no point in inventing cryptic symbols and discussing what their parsing precedence should be.
Just total bike-shedding.
ISAs that aren't legacy from 8/16 bit machines from the 70s/80s often don't even HAVE a rotate instruction anyway (or a carry flag).
The ARM processor has two rotate operations, ROR (Rotate Right) and RRX (Rotate Right with Extend).
The ARM core contains a Barrel shifter which takes a value to be shifted or rotated, an amount to shift or rotate by and the type of shift or rotate. This can be used by various classes of ARM instructions to perform comparatively complex operations in a single instruction. On ARMs up to and including the ARM6 family, instructions take no longer to execute by making use of the barrel shifter, unless the amount to be shifted is specified by a register, in which case the instruction will take an extra cycle to complete.
counter equals zero;
if counter greater than ten then
counter equals twenty;
total equals counter plus prior_count;
Multiway branchesConsider also including ranges as in the Oberon CASE statement e.g. using your syntax:
Ideas, suggestions about this feature are welcome.
Multiway branchesConsider also including ranges as in the Oberon CASE statement e.g. using your syntax:
Ideas, suggestions about this feature are welcome.
when ("a".."z","0"..9")
How many times is rotate used in programs? seriously? If no language offers a "rotate" operator then HOW THE **** COULD THERE BE any examples of it being used!!
Take a look at the ARM reference manual, you might learn something, clearly you've just learned something about ARM's ISA that you rather obviously didn't know.
This is just so stupid.
The number of times rotate is used in programs, there is absolutely no disadvantage in typing rotate_right_through_carry(x, i). There is no point in inventing cryptic symbols and discussing what their parsing precedence should be.
Just total bike-shedding.
ISAs that aren't legacy from 8/16 bit machines from the 70s/80s often don't even HAVE a rotate instruction anyway (or a carry flag).
Fettling with a language is a harmless hobby.
Knowing why that is the case would require that the OP understood the evolution that occurred then.
How many times is rotate used in programs? seriously? If no language offers a "rotate" operator then HOW THE **** COULD THERE BE any examples of it being used!!
Very very very rarely.
Rotate is used if and only if the algorithm calls for it, nothing at all to do with the programming language or instruction set.
QuoteTake a look at the ARM reference manual, you might learn something, clearly you've just learned something about ARM's ISA that you rather obviously didn't know.
I've been programming ARM CPUs in assembly language since the 1990s, maybe late 80s.
Knowing why that is the case would require that the OP understood the evolution that occurred then.
I'm curious what combination of characters he is planning to use for:
- CRC32 instruction
- DES and AES instructions
- SHA
- bit reversal in a word
- byte reversal
- selecting bits from one or another source based on a mask
I could go on. There is an infinity of operations, and even hundreds of operations that have been implemented as instructions in some ISA or another. Each very handy and performance-enhancing when needed -- which might be exactly once on the whole machine, in one function in the (hand-written) standard library.
Rotates are a very small step away from shifts, very very small differenceThey are not though. Shifts have the same meaning for various word lengths. Rotates are dependent on the type of the underlying value, so the result depends on how abstract machine and types are implemented. And that also poorly maps on most hardware.
What about XOR? what symbols (if any!) would one use for that in a new language striving to be more helpful than C on hardware programming?C has XOR.
And you calling people to look at things is just funny. It is good that you are discovering all those things and all, but it just looks really funny from outside.
Rotates are a very small step away from shifts, very very small differenceThey are not though. Shifts have the same meaning for various word lengths. Rotates are dependent on the type of the underlying value, so the result depends on how abstract machine and types are implemented. And that also poorly maps on most hardware.
And what you are doing is really bike-shedding. None of what you are doing is important in a language design.What about XOR? what symbols (if any!) would one use for that in a new language striving to be more helpful than C on hardware programming?C has XOR.
And you calling people to look at things is just funny. It is good that you are discovering all those things and all, but it just looks really funny from outside.
Rotate right (immediate) provides the value of the contents of a register rotated by a variable number of bits. The bits that are rotated off the right end are inserted into the vacated bit positions on the left.
Not as I'm currently defining these operations. I define rotate exactly as it is defined here:That ARM operation is only defined for 32-bit words. What if I want to rotate a byte?
As for "funny" what's funny is that Mr. Halt claimed almost no processors support rotate yet I showed him that ARM actually does, and you claiming that rotate is not very close to shift until I - just now - showed you that it is.LOL
Rotates are a very small step away from shifts, very very small differenceThey are not though. Shifts have the same meaning for various word lengths. Rotates are dependent on the type of the underlying value, so the result depends on how abstract machine and types are implemented. And that also poorly maps on most hardware.
And what you are doing is really bike-shedding. None of what you are doing is important in a language design.What about XOR? what symbols (if any!) would one use for that in a new language striving to be more helpful than C on hardware programming?C has XOR.
And you calling people to look at things is just funny. It is good that you are discovering all those things and all, but it just looks really funny from outside.
Knowing why that is the case would require that the OP understood the evolution that occurred then.
I'm curious what combination of characters he is planning to use for:
- CRC32 instruction
- DES and AES instructions
- SHA
- bit reversal in a word
- byte reversal
- selecting bits from one or another source based on a mask
I could go on. There is an infinity of operations, and even hundreds of operations that have been implemented as instructions in some ISA or another. Each very handy and performance-enhancing when needed -- which might be exactly once on the whole machine, in one function in the (hand-written) standard library.
And what you are doing is really bike-shedding. None of what you are doing is important in a language design.
And you calling people to look at things is just funny. It is good that you are discovering all those things and all, but it just looks really funny from outside.
You're being ignored.
ISAs that aren't legacy from 8/16 bit machines from the 70s/80s often don't even HAVE a rotate instruction anyway (or a carry flag).That seems like a bit of an exaggeration. MIPS, Alpha, and RISC-V lack carry flags, but ARM, Sparc, and PPC all have one (but sparc doesn't have Rotate.)
And what you are doing is really bike-shedding. None of what you are doing is important in a language design.
QuoteISAs that aren't legacy from 8/16 bit machines from the 70s/80s often don't even HAVE a rotate instruction anyway (or a carry flag).That seems like a bit of an exaggeration. MIPS, Alpha, and RISC-V lack carry flags, but ARM, Sparc, and PPC all have one (but sparc doesn't have Rotate.)
(Carry seems like a lot to give up. If the CC register is a potential bottleneck, how come you don't see it implemented by adding an extra bit to each register?)
Unless you're dealing with "big integers", arithmetic operations with carry are not that usefulI guess. But aren't "big integer" operations become more common, what with assorted crypto stuff?
Using 63 bits per limb in your bignum code instead of 64 gives the same effect.I guess. I was imagining a small number of instructions that would access the 65th bit, so that the normal instruction set could be used while being blissfully unaware that the 65th bit was there.
It was probably debated to death elsewhere, but it is important to differentiate instruction set from the microarchitecure. Typical RISC-V add/sltu pairs may be more code than ARM's add/adc. but they don't have to take more time to execute if performance matters. Those instructions may be fused and executed together. But symmetrical register-only architecture is potentially easier to implement in heavily pipelined and OOO designs. It is hard to tell which one is really better.
X86 is pretty bad ISA, yet until recently its implementations absolutely destroyed everything else. Then Apple managed to optimize ARM quite well. I'm not sure if someone would manage to get anything close from RISC-V. We'll have to wait and see.
I would personally go for the extra bit per register option myself as an extension for my own core.This only works as long as you are not using it in any other context. As soon as you want to branch on that carry bit, you are screwed again. And the add/adc use seems pretty limited to bother.
If there is no body that you are listening to, why are you continuing to post in this thread?
The last thing I really liked having Carry and ADC for was an efficient implementation of the IP checksum.
This is exactly why I believe it is so important to examine what kind of machine code one wants a compiler to produce.
Typical RISC-V add/sltu pairs may be more code than ARM's add/adc
A programming language for embedded systems that is targeted for safety-critical and mission-critical systems would likely have the following features:
1. Memory safety: The language would have built-in mechanisms to prevent common memory-related errors such as buffer overflows, use-after-free, and null pointer dereferences.
2. Type safety: The language would have a strong type system that helps prevent type-related errors, such as type casting errors and implicit type conversions.
3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
4. Automatic memory management: The language would have a garbage collector or other mechanism for automatic memory management, which would help prevent memory leaks and other related errors.
5. Strict syntax and semantics: The language would have a strict syntax and semantics, in order to minimize the chances of introducing new errors.
6. Formal verification: The language would have formal verification tools that could be used to prove the absence of certain types of errors, such as data races.
7. Strong error handling: The language would have strong error handling, in order to prevent errors from propagating and causing further issues.
8. Support for concurrency: The language would have built-in support for concurrency, in order to make it easier to write multi-threaded code that is also safe.
9. Support for functional programming: The language would support functional programming features such as immutable variables, closures, and higher-order functions, which would help make the code more predictable and less error-prone.
10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
11. Support for real-time constraints: The language would have built-in support for real-time constraints, such as hard real-time and soft real-time, allowing developers to specify time constraints and deadlines for specific operations.
12. Interoperability: The language would have the ability to interoperate with other languages, such as C and C++, which are commonly used in embedded systems. This would allow for the use of existing libraries and codebases, and make it easier to integrate the new language with existing systems.
13. Standard libraries: The language would have a comprehensive set of standard libraries, including data structures and algorithms, for use in embedded systems.
14. Compact and efficient: The language would be designed to be compact and efficient, in order to minimize memory and processing requirements, which are important considerations in embedded systems.
15. Security features: The language would have built-in security features such as secure data handling and encryption to protect against common security threats.
16. Tooling and debugging: The language would have a robust set of tooling and debugging options, allowing developers to easily locate and fix errors.
17. Documentations: The language would have comprehensive and up-to-date documentation, including tutorials, guides, and reference material, to help developers get started quickly and become proficient with the language.
18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
Overall, the language would be designed with safety and reliability in mind, with a focus on preventing errors and ensuring that the code is predictable and easy to understand. It would also be efficient and flexible enough to meet the specific requirements of embedded systems.
19. Certification: The language would be designed to meet industry standards and certifications for safety-critical systems, such as ISO 26262 for automotive systems and DO-178C for avionics systems.
20. Safety-critical libraries: The language would have a set of libraries specifically designed for safety-critical systems that provide functionality such as fault-tolerance and redundancy.
21. Safety-critical constructs: The language would have constructs such as assertions and monitors that can be used to express safety properties and to check them at runtime.
22. Model-based development: The language would support model-based development, which allows for the design, verification, and validation of safety-critical systems using formal methods.
23. Traceability: The language would have traceability features that allow developers to trace requirements, design, and implementation, which is critical for safety-critical systems.
24. Auditing and logging: The language would have built-in support for auditing and logging which allows to check and trace the actions of the system and detect malfunctions that could cause safety hazards.
25. Testability: The language would be designed with testability in mind, allowing developers to easily create and execute unit tests, integration tests, and system tests to verify the correct functioning of the system.
Overall, the new programming language for embedded systems would be designed to meet the specific needs of safety-critical and mission-critical systems, providing a combination of safety, reliability, and efficiency, while at the same time meeting industry standards and certifications.
26. Design by Contract: The language would support Design by Contract (DbC) paradigm, which allows developers to express preconditions, postconditions, and class invariants that specify the expected behavior of the functions and classes, making it easier to understand and reason about the code.
27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
28. Code review: The language would have tools and processes in place to support formal code reviews, where code is reviewed by other developers, in order to catch errors and improve the quality of the code.
29. Built-in safety mechanisms: The language would have built-in safety mechanisms such as watchdog timers, which can automatically reset the system if it becomes unresponsive, and fail-safe modes, which can be activated if a critical error is detected.
30. Support for formal verification: The language would support formal verification using tools such as model checkers, theorem provers, and static analyzers, which can be used to prove that the code meets certain safety properties.
31. Support for safety standards: The language would be designed to support safety standards such as IEC 61508 and ISO 26262, which provide guidelines for the development of safety-critical systems.
Overall, the new programming language for embedded systems would provide a comprehensive set of features for safety-critical and mission-critical systems, including memory safety, type safety, array bounds checking, automatic memory management, strict syntax and semantics, formal verification, strong error handling, support for concurrency, functional programming, and hardware-level control. Additionally, it would have features to support real-time constraints, interoperability, standard libraries, compact and efficient, security, tooling and debugging, documentation, community support and safety-critical libraries, constructs, model-based development, traceability, auditing and logging, testability, Design by Contract, static analysis, code review, built-in safety mechanisms, support for formal verification and safety standards.
I was imagining a small number of instructions that would access the 65th bit, so that the normal instruction set could be used while being blissfully unaware that the 65th bit was there.
I asked ChatGPT
I asked ChatGPT
Better get a job in avionics.
Really, you are thinking that good design practices are beneficial only in avionics?
I asked ChatGPT "Let's assume that you need to design a new programming language for embedded systems that will be targeted for safety-critical and mission-critical embedded systems, and provide safe memory handling and safe array operators. What would it be like?"I don't know if AI will replace programmers anytime soon, but it will be a godsend for producing marketing materials :D
Also, make sure that the new programming language will meet requirements 5 and 18.
18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
Rotate operations can be performed in your code, despite the C language not including a rotate operator.
The compiler will detect expressions that implement rotate operations using shift and logical operators and compile
them efficiently.
For the following code:
c = (c << 1) | (c >> 7);
if c is unsigned and non-volatile, the compiler will detect that the intended operation is a rotate left of 1 bit and will
encode the output using the PIC MCU rotate instructions. A rotate left of 2 bits would be implemented with code like:
c = (c << 2) | (c >> 6);
This code optimization will also work for integral types larger than a char. If the optimization cannot be applied, or
this code is ported to another compiler, the rotate will be implemented, but typically with shifts and a bitwise OR
operation.
Also, make sure that the new programming language will meet requirements 5 and 18.
:DQuote18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
impossible, everyone in the community will soon be in the ignore list
I asked ChatGPT "Let's assume that you need to design a new programming language for embedded systems that will be targeted for safety-critical and mission-critical embedded systems, and provide safe memory handling and safe array operators. What would it be like?"I don't know if AI will replace programmers anytime soon, but it will be a godsend for producing marketing materials :D
Really, you are thinking that good design practices are beneficial only in avionics?
No, I think real life problems need real life training, while putting a list of things taken from Wikipedia (never seen in action) is... frankly not even close to learning anything useful.
I think ChatGPT gave pretty good answer, considering the fact it took only few minutes to get that list. I would consider the current state of AI as an assisting technology, and the user needs to be able to evaluate the answers for sanity, correctness and usefulness.It gave you the answers you wanted to hear, which is its whole purpose ;)
I think ChatGPT gave pretty good answer, considering the fact it took only few minutes to get that list. I would consider the current state of AI as an assisting technology, and the user needs to be able to evaluate the answers for sanity, correctness and usefulness.It gave you the answers you wanted to hear, which is its whole purpose ;)
The holy grail of marketing is here. But ask it to actually design something new and then :scared:
Quote18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
impossible, everyone in the community will soon be in the ignore list
//Rotate Word Right (SmartMIPS Crypto engine)
//The contents of the low-order 32-bit word of GPR rt are rotated right
//the word result is placed in GPR rd.
//The bit-rotate amount is specified by sa
ROTR rd, rt, sa
but if you tell your assemble that your CPU is MIPS3 (.cpu MIPS3) then assembler compiler will accept it as "pseudoinstruction" that will replace with a sequence.I think ChatGPT gave pretty good answer, considering the fact it took only few minutes to get that list. I would consider the current state of AI as an assisting technology, and the user needs to be able to evaluate the answers for sanity, correctness and usefulness.It gave you the answers you wanted to hear, which is its whole purpose ;)
The holy grail of marketing is here. But ask it to actually design something new and then :scared:
And, what would be your answer to that same question then? According to your reply, you have some other properties that are more important in a programming language.
If you think about C and C++ languages for example, there are many recommendations and guidelines for producing maintainable and less buggy software (I am not talking about naming conventions and formatting here). For example MISRA-C and C++ contain a lot of recommendations that are result of a poor language design in the first place.
MISRA C/C++
If you think about C and C++ languages for example, there are many recommendations and guidelines for producing maintainable and less buggy software (I am not talking about naming conventions and formatting here). For example MISRA-C and C++ contain a lot of recommendations that are result of a poor language design in the first place.
MISRA C/C++
Sure, but ... MISRA is *ONLY* about the code :D
In avionics it's DO178B level { A .. E }, so it's MISRA integrated with ICE + Eng + test-report + QA, all applied to the whole software lifecycle.
Wiki pages and university courses can simply list concepts that you can also consider separately, it doesn't make any problem, but real life avionics has to consider all the specific know-how here and there, but all reflected among both developers and testers.
So, you understand WHY you have to write your C or Ada code in a specific way. Usually, because it's useful for your ICE-AI, and/or for testers, or something that is not *MISRA* specific, but actually helps a lot with the software life-cycle.
It's like The Matrix: talking about that is not like living it. You need to see yourself.
(o'man, am I really red-pilling? or something?
Why do I feel like Morpheus? Goshh :o :o :o )
I asked ChatGPT "Let's assume that you need to design a new programming language for embedded systems that will be targeted for safety-critical and mission-critical embedded systems, and provide safe memory handling and safe array operators. What would it be like?"QuoteA programming language for embedded systems that is targeted for safety-critical and mission-critical systems would likely have the following features:
1. Memory safety: The language would have built-in mechanisms to prevent common memory-related errors such as buffer overflows, use-after-free, and null pointer dereferences.
2. Type safety: The language would have a strong type system that helps prevent type-related errors, such as type casting errors and implicit type conversions.
3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
4. Automatic memory management: The language would have a garbage collector or other mechanism for automatic memory management, which would help prevent memory leaks and other related errors.
5. Strict syntax and semantics: The language would have a strict syntax and semantics, in order to minimize the chances of introducing new errors.
6. Formal verification: The language would have formal verification tools that could be used to prove the absence of certain types of errors, such as data races.
7. Strong error handling: The language would have strong error handling, in order to prevent errors from propagating and causing further issues.
8. Support for concurrency: The language would have built-in support for concurrency, in order to make it easier to write multi-threaded code that is also safe.
9. Support for functional programming: The language would support functional programming features such as immutable variables, closures, and higher-order functions, which would help make the code more predictable and less error-prone.
10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
11. Support for real-time constraints: The language would have built-in support for real-time constraints, such as hard real-time and soft real-time, allowing developers to specify time constraints and deadlines for specific operations.
12. Interoperability: The language would have the ability to interoperate with other languages, such as C and C++, which are commonly used in embedded systems. This would allow for the use of existing libraries and codebases, and make it easier to integrate the new language with existing systems.
13. Standard libraries: The language would have a comprehensive set of standard libraries, including data structures and algorithms, for use in embedded systems.
14. Compact and efficient: The language would be designed to be compact and efficient, in order to minimize memory and processing requirements, which are important considerations in embedded systems.
15. Security features: The language would have built-in security features such as secure data handling and encryption to protect against common security threats.
16. Tooling and debugging: The language would have a robust set of tooling and debugging options, allowing developers to easily locate and fix errors.
17. Documentations: The language would have comprehensive and up-to-date documentation, including tutorials, guides, and reference material, to help developers get started quickly and become proficient with the language.
18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
Overall, the language would be designed with safety and reliability in mind, with a focus on preventing errors and ensuring that the code is predictable and easy to understand. It would also be efficient and flexible enough to meet the specific requirements of embedded systems.
19. Certification: The language would be designed to meet industry standards and certifications for safety-critical systems, such as ISO 26262 for automotive systems and DO-178C for avionics systems.
20. Safety-critical libraries: The language would have a set of libraries specifically designed for safety-critical systems that provide functionality such as fault-tolerance and redundancy.
21. Safety-critical constructs: The language would have constructs such as assertions and monitors that can be used to express safety properties and to check them at runtime.
22. Model-based development: The language would support model-based development, which allows for the design, verification, and validation of safety-critical systems using formal methods.
23. Traceability: The language would have traceability features that allow developers to trace requirements, design, and implementation, which is critical for safety-critical systems.
24. Auditing and logging: The language would have built-in support for auditing and logging which allows to check and trace the actions of the system and detect malfunctions that could cause safety hazards.
25. Testability: The language would be designed with testability in mind, allowing developers to easily create and execute unit tests, integration tests, and system tests to verify the correct functioning of the system.
Overall, the new programming language for embedded systems would be designed to meet the specific needs of safety-critical and mission-critical systems, providing a combination of safety, reliability, and efficiency, while at the same time meeting industry standards and certifications.
26. Design by Contract: The language would support Design by Contract (DbC) paradigm, which allows developers to express preconditions, postconditions, and class invariants that specify the expected behavior of the functions and classes, making it easier to understand and reason about the code.
27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
28. Code review: The language would have tools and processes in place to support formal code reviews, where code is reviewed by other developers, in order to catch errors and improve the quality of the code.
29. Built-in safety mechanisms: The language would have built-in safety mechanisms such as watchdog timers, which can automatically reset the system if it becomes unresponsive, and fail-safe modes, which can be activated if a critical error is detected.
30. Support for formal verification: The language would support formal verification using tools such as model checkers, theorem provers, and static analyzers, which can be used to prove that the code meets certain safety properties.
31. Support for safety standards: The language would be designed to support safety standards such as IEC 61508 and ISO 26262, which provide guidelines for the development of safety-critical systems.
Overall, the new programming language for embedded systems would provide a comprehensive set of features for safety-critical and mission-critical systems, including memory safety, type safety, array bounds checking, automatic memory management, strict syntax and semantics, formal verification, strong error handling, support for concurrency, functional programming, and hardware-level control. Additionally, it would have features to support real-time constraints, interoperability, standard libraries, compact and efficient, security, tooling and debugging, documentation, community support and safety-critical libraries, constructs, model-based development, traceability, auditing and logging, testability, Design by Contract, static analysis, code review, built-in safety mechanisms, support for formal verification and safety standards.
This is a good list to keep in mind when designing a new programming language. Take existing programming languages used in safety-critical embedded systems (such as C, C++, Ada and Rust), implement their good features, and leave out and fix their problematic features. Also, make sure that the new programming language will meet requirements 5 and 18.
Also, make sure that the new programming language will meet requirements 5 and 18.
I wanted to limit my writings mostly into programming language-domain, as this thread is about a [new] programming language. Of course designing and implementing an embedded system is much more than just writing code with a specific programming language.
...
We have already 70+ years worth of experience of different programming languages. A new programming language should take the good parts of the existing languages, and leave out or fix the bad parts. It is not a very clever idea to repeat the errors and faults of existing and past programming languages, if they could be prevented in the first place with some careful planning.
Sure, here are my two most desirable features of any software product:I think ChatGPT gave pretty good answer, considering the fact it took only few minutes to get that list. I would consider the current state of AI as an assisting technology, and the user needs to be able to evaluate the answers for sanity, correctness and usefulness.It gave you the answers you wanted to hear, which is its whole purpose ;)
The holy grail of marketing is here. But ask it to actually design something new and then :scared:
And, what would be your answer to that same question then? According to your reply, you have some other properties that are more important in a programming language.
Collecting a list of desirable features is the easiest 1% of the job of designing software, often already done for you by others.
The hard part is knowing what can't be done, what must be compromised and how to realize customer's goals while staying within the annoying limits of reality.
Also, the customer is usually having an XY problem and it's better to realize it sooner than later.
And speaking of the concrete output from the bot, I am personally quite far from anything really safety critical, but here's my $.02:
- I don't think functional programming is common practice in embedded, but the bot picked it up because f.p. fanboys surely talk about it a lot
- formal verification is a hell difficult problem being worked on since the field's inception, show us your concrete ideas, please, because anyone can simply name-drop it
- not sure what "built-in encryption to protect against common security threats" means in a programming language
- what sort of "tools and processes" you need in a language to support code review?
- watchdog timers built into a programming language - I think it got carried away a little
Also, make sure that the new programming language will meet requirements 5 and 18.
:DQuote18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
impossible, everyone in the community will soon be in the ignore list
===============================================
By the way, Even though there is no rotate operator in C, compilers that target actitectures that have rotate instructions usually recognize particular constructs and translate them into rotate instructionsQuote from: XC8 Compiler ManualRotate operations can be performed in your code, despite the C language not including a rotate operator.
The compiler will detect expressions that implement rotate operations using shift and logical operators and compile
them efficiently.
For the following code:
c = (c << 1) | (c >> 7);
if c is unsigned and non-volatile, the compiler will detect that the intended operation is a rotate left of 1 bit and will
encode the output using the PIC MCU rotate instructions. A rotate left of 2 bits would be implemented with code like:
c = (c << 2) | (c >> 6);
This code optimization will also work for integral types larger than a char. If the optimization cannot be applied, or
this code is ported to another compiler, the rotate will be implemented, but typically with shifts and a bitwise OR
operation.
or provide a builtin that map to the dedicated instruction (but you usually want to use the rotate in algorythms you are already writing in assembly sooooo.....)
Quote18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
impossible, everyone in the community will soon be in the ignore list
This, was quite hilarious seeing the OP rage-ignoring the few world class experts who happen to hang on this forum ;D. (You know who you are, of course.)
I can totally see in my mind where OP's "experience" is coming from: working as a middle manager who simply fires everybody who says "no" to his absolutely stupid ideas, and collects a bunch of so called "yes men" ( https://www.urbandictionary.com/define.php?term=yes%20men (https://www.urbandictionary.com/define.php?term=yes%20men) ) around him. And there is no shortage of yes men.
This is the classic pattern of destroying the productivity of a business, ignoring those who do understand, and spending all the time discussing the color and construction materials of the bike shed and micromanaging this discussion. And the business can appear to still run, based on buying out smaller companies with their product lines which are profitable for some time before getting destroyed. And/or outsourcing projects that need to be done, to smaller companies with different mindset.
I asked ChatGPT "Let's assume that you need to design a new programming language for embedded systems that will be targeted for safety-critical and mission-critical embedded systems, and provide safe memory handling and safe array operators. What would it be like?"QuoteA programming language for embedded systems that is targeted for safety-critical and mission-critical systems would likely have the following features:
1. Memory safety: The language would have built-in mechanisms to prevent common memory-related errors such as buffer overflows, use-after-free, and null pointer dereferences.
2. Type safety: The language would have a strong type system that helps prevent type-related errors, such as type casting errors and implicit type conversions.
3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
4. Automatic memory management: The language would have a garbage collector or other mechanism for automatic memory management, which would help prevent memory leaks and other related errors.
5. Strict syntax and semantics: The language would have a strict syntax and semantics, in order to minimize the chances of introducing new errors.
6. Formal verification: The language would have formal verification tools that could be used to prove the absence of certain types of errors, such as data races.
7. Strong error handling: The language would have strong error handling, in order to prevent errors from propagating and causing further issues.
8. Support for concurrency: The language would have built-in support for concurrency, in order to make it easier to write multi-threaded code that is also safe.
9. Support for functional programming: The language would support functional programming features such as immutable variables, closures, and higher-order functions, which would help make the code more predictable and less error-prone.
10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
11. Support for real-time constraints: The language would have built-in support for real-time constraints, such as hard real-time and soft real-time, allowing developers to specify time constraints and deadlines for specific operations.
12. Interoperability: The language would have the ability to interoperate with other languages, such as C and C++, which are commonly used in embedded systems. This would allow for the use of existing libraries and codebases, and make it easier to integrate the new language with existing systems.
13. Standard libraries: The language would have a comprehensive set of standard libraries, including data structures and algorithms, for use in embedded systems.
14. Compact and efficient: The language would be designed to be compact and efficient, in order to minimize memory and processing requirements, which are important considerations in embedded systems.
15. Security features: The language would have built-in security features such as secure data handling and encryption to protect against common security threats.
16. Tooling and debugging: The language would have a robust set of tooling and debugging options, allowing developers to easily locate and fix errors.
17. Documentations: The language would have comprehensive and up-to-date documentation, including tutorials, guides, and reference material, to help developers get started quickly and become proficient with the language.
18. Community support: The language would have a strong and active community of developers, providing support and contributing to the language's development and maintenance.
Overall, the language would be designed with safety and reliability in mind, with a focus on preventing errors and ensuring that the code is predictable and easy to understand. It would also be efficient and flexible enough to meet the specific requirements of embedded systems.
19. Certification: The language would be designed to meet industry standards and certifications for safety-critical systems, such as ISO 26262 for automotive systems and DO-178C for avionics systems.
20. Safety-critical libraries: The language would have a set of libraries specifically designed for safety-critical systems that provide functionality such as fault-tolerance and redundancy.
21. Safety-critical constructs: The language would have constructs such as assertions and monitors that can be used to express safety properties and to check them at runtime.
22. Model-based development: The language would support model-based development, which allows for the design, verification, and validation of safety-critical systems using formal methods.
23. Traceability: The language would have traceability features that allow developers to trace requirements, design, and implementation, which is critical for safety-critical systems.
24. Auditing and logging: The language would have built-in support for auditing and logging which allows to check and trace the actions of the system and detect malfunctions that could cause safety hazards.
25. Testability: The language would be designed with testability in mind, allowing developers to easily create and execute unit tests, integration tests, and system tests to verify the correct functioning of the system.
Overall, the new programming language for embedded systems would be designed to meet the specific needs of safety-critical and mission-critical systems, providing a combination of safety, reliability, and efficiency, while at the same time meeting industry standards and certifications.
26. Design by Contract: The language would support Design by Contract (DbC) paradigm, which allows developers to express preconditions, postconditions, and class invariants that specify the expected behavior of the functions and classes, making it easier to understand and reason about the code.
27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
28. Code review: The language would have tools and processes in place to support formal code reviews, where code is reviewed by other developers, in order to catch errors and improve the quality of the code.
29. Built-in safety mechanisms: The language would have built-in safety mechanisms such as watchdog timers, which can automatically reset the system if it becomes unresponsive, and fail-safe modes, which can be activated if a critical error is detected.
30. Support for formal verification: The language would support formal verification using tools such as model checkers, theorem provers, and static analyzers, which can be used to prove that the code meets certain safety properties.
31. Support for safety standards: The language would be designed to support safety standards such as IEC 61508 and ISO 26262, which provide guidelines for the development of safety-critical systems.
Overall, the new programming language for embedded systems would provide a comprehensive set of features for safety-critical and mission-critical systems, including memory safety, type safety, array bounds checking, automatic memory management, strict syntax and semantics, formal verification, strong error handling, support for concurrency, functional programming, and hardware-level control. Additionally, it would have features to support real-time constraints, interoperability, standard libraries, compact and efficient, security, tooling and debugging, documentation, community support and safety-critical libraries, constructs, model-based development, traceability, auditing and logging, testability, Design by Contract, static analysis, code review, built-in safety mechanisms, support for formal verification and safety standards.
This is a good list to keep in mind when designing a new programming language. Take existing programming languages used in safety-critical embedded systems (such as C, C++, Ada and Rust), implement their good features, and leave out and fix their problematic features. Also, make sure that the new programming language will meet requirements 5 and 18.
I've only explicitly ignored posters who are demonstrably insulting
Here we go again, you actually want me to ignore you, another rude insulting rant (emphasis added by me) where you attack demons of your own making. You're ignored for abusive and demeaning language.
sublime!
uint32_t rotl32a (uint32_t x, uint32_t n)
{
return (x<<n) | (x>>(32-n));
}
That's basically where what @Jportici wrote came from :o :o :o3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
Here I think we can support two modes, fast and slow. The fast simply ensures that the referenced element lies inside the array, it might be accessed with illegal subscripts, be the wrong element, but it is inside the array. The slow verifies each subscript against the arrays defined bounds. In a 10x10 array for example accessing [2,12] is wrong but won't corrupt system memory (only user data memory). These checks are not hard to do and some CPUs even offer instructions to assist. This capability could also be enabled/disabled at runtime, perhaps even on a per-array basis...
10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
This needs better definition IMHO. The language does offer "bit" as a data type and supports declaring arrays of bits and various alignment options too. But is there more too this? What does "low level" really mean? I can see scope for abstract access to the stack, some operations that might allow stack walking perhaps but we soon get to target specific stuff which is undesirable.
27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
This is good, I've not thought much about this though, so this needs more definition perhaps...
if a < b < c > d then
log("Conditions Met.");
if a < b && b < c && c > d then
log("Conditions Met.");
3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
Here I think we can support two modes, fast and slow. The fast simply ensures that the referenced element lies inside the array, it might be accessed with illegal subscripts, be the wrong element, but it is inside the array. The slow verifies each subscript against the arrays defined bounds. In a 10x10 array for example accessing [2,12] is wrong but won't corrupt system memory (only user data memory). These checks are not hard to do and some CPUs even offer instructions to assist. This capability could also be enabled/disabled at runtime, perhaps even on a per-array basis...
When passing an array to a function, the compiler should also pass the the length of the array to the function, so that the function can obtain the length of the array at the run-time.
For example, in C this needs to be done manually by introducing an additional parameter to the function. Also, any array bounds checking needs to be done manually. It is very easy to produce off-by-one array access errors in C.
The programmer should be able to enable/disable the compiler-generated array bounds checking. During unit testing, the programmer may want to enable all checks for best test coverage. For the production code, the programmer may want to disable the bounds checking selectively in some parts of the code.10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
This needs better definition IMHO. The language does offer "bit" as a data type and supports declaring arrays of bits and various alignment options too. But is there more too this? What does "low level" really mean? I can see scope for abstract access to the stack, some operations that might allow stack walking perhaps but we soon get to target specific stuff which is undesirable.
I read this as "the programming language should have means to define and access the peripheral registers with an ease".
Typically the operations for peripheral registers are reading/writing a register, and testing/setting/clearing/toggling a bit or set of bits in the register.
The language should have support for defining bits and bitmasks for the registers, and prevent using a (wrong) bit mask defined for another register.
This whole register access-topic is quite complex, and needs careful analysis to get it right. For example, some registers may be read-only, some write-only, and some register may contain bits that are read-only, etc. It would be a good thing if the programming language had support for expressing these things, and the compiler could check for proper register and bit accesses. At this point I do not have any good ideas for this, though.27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
This is good, I've not thought much about this though, so this needs more definition perhaps...
This might be tricky, and I know very little about this topic. However, using compile-time static asserts/checks, and checks/asserts during runtime with preconditions, post-conditions and other checks could help conveying the information how the functions are expected to behave and what kind of parameter values functions are expecting. These run-time checks can be enabled during unit testing, and selectively turned off in the production code. The compiler should be able to ascertain that there are no unwanted side effects when performing these checks.
procedure sort_table (table);
arg table(*,*,*) bin(15);
end;
sublime!
talking about sublime, I checked an old copy of my Dr. Dobb's Journal, and it says ROL and ROR were useful during i386 era for bit-by-bit examining a value in a way that is non-information-destructive.
Kind of bit(x)-test for intel chips :o :o :o
Then it points out something like this (https://blog.regehr.org/archives/1063). Nice, isn't it?
("Safe, Efficient, and Portable Rotate in C/C++")Code: [Select]uint32_t rotl32a (uint32_t x, uint32_t n)
That's basically where what @Jportici wrote came from :o :o :o
{
return (x<<n) | (x>>(32-n));
}
3. Array bounds checking: The language would have built-in array bounds checking to prevent out-of-bounds errors when accessing arrays.
Here I think we can support two modes, fast and slow. The fast simply ensures that the referenced element lies inside the array, it might be accessed with illegal subscripts, be the wrong element, but it is inside the array. The slow verifies each subscript against the arrays defined bounds. In a 10x10 array for example accessing [2,12] is wrong but won't corrupt system memory (only user data memory). These checks are not hard to do and some CPUs even offer instructions to assist. This capability could also be enabled/disabled at runtime, perhaps even on a per-array basis...
When passing an array to a function, the compiler should also pass the the length of the array to the function, so that the function can obtain the length of the array at the run-time.
For example, in C this needs to be done manually by introducing an additional parameter to the function. Also, any array bounds checking needs to be done manually. It is very easy to produce off-by-one array access errors in C.
The programmer should be able to enable/disable the compiler-generated array bounds checking. During unit testing, the programmer may want to enable all checks for best test coverage. For the production code, the programmer may want to disable the bounds checking selectively in some parts of the code.10. Support for hardware-level control: The language would have support for interacting with low-level hardware, in order to allow for the precise control that is often required in embedded systems.
This needs better definition IMHO. The language does offer "bit" as a data type and supports declaring arrays of bits and various alignment options too. But is there more too this? What does "low level" really mean? I can see scope for abstract access to the stack, some operations that might allow stack walking perhaps but we soon get to target specific stuff which is undesirable.
I read this as "the programming language should have means to define and access the peripheral registers with an ease".
Typically the operations for peripheral registers are reading/writing a register, and testing/setting/clearing/toggling a bit or set of bits in the register.
The language should have support for defining bits and bitmasks for the registers, and prevent using a (wrong) bit mask defined for another register.
This whole register access-topic is quite complex, and needs careful analysis to get it right. For example, some registers may be read-only, some write-only, and some register may contain bits that are read-only, etc. It would be a good thing if the programming language had support for expressing these things, and the compiler could check for proper register and bit accesses. At this point I do not have any good ideas for this, though.27. Static analysis: The language would have built-in static analysis tools that can analyze the code and detect potential errors, such as null pointer dereferences, race conditions, and buffer overflows, before the code is even run.
This is good, I've not thought much about this though, so this needs more definition perhaps...
This might be tricky, and I know very little about this topic. However, using compile-time static asserts/checks, and checks/asserts during runtime with preconditions, post-conditions and other checks could help conveying the information how the functions are expected to behave and what kind of parameter values functions are expecting. These run-time checks can be enabled during unit testing, and selectively turned off in the production code. The compiler should be able to ascertain that there are no unwanted side effects when performing these checks.
sublime!
talking about sublime, I checked an old copy of my Dr. Dobb's Journal, and it says ROL and ROR were useful during i386 era for bit-by-bit examining a value in a way that is non-information-destructive.
Kind of bit(x)-test for intel chips :o :o :o
Then it points out something like this (https://blog.regehr.org/archives/1063). Nice, isn't it?
("Safe, Efficient, and Portable Rotate in C/C++")Code: [Select]uint32_t rotl32a (uint32_t x, uint32_t n)
That's basically where what @Jportici wrote came from :o :o :o
{
return (x<<n) | (x>>(32-n));
}
I'm sure it was well known long before Dr Dobbs.
Now that ChatGPT has been involved in this process, I think we're gonna reach the sublime!
Comparison Chaining
I've been reading about how some languages support comparison operator chaining. Now the grammars of most languages support it, it is a semantic question really.Code: [Select]
if a < b < c > d then
log("Conditions Met.");
This can be interpreted asCode: [Select]
if a < b && b < c && c > d then
log("Conditions Met.");
I find this quite appealing, thoughts?
Comparison Chaining
I've been reading about how some languages support comparison operator chaining. Now the grammars of most languages support it, it is a semantic question really.Code: [Select]
if a < b < c > d then
log("Conditions Met.");
This can be interpreted asCode: [Select]
if a < b && b < c && c > d then
log("Conditions Met.");
I find this quite appealing, thoughts?
it may be that i use too much C but i do not want to think about precedence, even when writing other languages.
So i use parentheses
I probably still use this notation only when writing on paper
dcl counter bin(15);
counter <= 123;
if counter = 321 then
return;
namespace test;
dcl root_ptr pointer;
procedure outer (arg);
dcl counter bin(15);
dcl window bin(31);
if counter = 123 then
return;
end;
end procedure;
procedure rerun (arg);
call s$system.memory.allocate_virgin(10 0000:H); // 1,048,576 in dec FYI
end procedure;
end;
namespace utility.sundry;
// an empty namespace
end;
namespace s$system;
namespace memory;
procedure allocate_virgin(size);
arg size bin(31);
end;
end;
end;
Moderators: please can I stop notifications from this to thread? I've learned my lesson.
Moderators: please can I stop notifications from this to thread? I've learned my lesson.
def devices enum bin(15),
gps = 10,
adc,
spi
end;
def folders enum string(32),
devices = "devices",
logs = "logs,
dumps = "dumps"
end;
def rates enum,
fast,
medium,
slow,
stopped
end;
dcl mydata folders; // dcl an instance of the "folders" enum
<dcl-keyword> <identifer> [multiple other attributes in any order] <semicolon>
type baud_rates enum string(31);
dcl rates as baud_rates;
type baud_rates enum bit(8),
first = 1010 0011:b,
second = 1110 1101:b,
third = 1101 0111:b,
fourth = 0010 1100:b
end;
Moderators: please can I stop notifications from this to thread? I've learned my lesson.See the buttons at the top right and bottom right of the entire thread: Reply, Notify/Unnotify, Mark unread, Send this topic, Print, Search?
If you see Notify, click on it, click Yes, and then it changes to Unnotify. Click Unnotify, and Yes, and no more notifications!
(Even if new posts are made to this thread, it will not show up in the New Replies to Your Posts list.)
No, that does not work. It controls notification emails or something, not "Show new replies to your posts", which I believe it is impossible to disable for a given thread.You're right. I mixed the darn things up!
So being able to define something as (in an arbitrary language) binary [8\8] would be nice, an 8 bit integer part and an 8 bit fraction part. [...] Is this already a thing, or is it too obscure to be worried about?I have implemented something like that in Python, deriving from tuple. Arithmetic, unary, and binary operators are just syntactic sugar on top of special methods (https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types), so that e.g. a*b is equivalent to a.__mul__(b) (or if that is NotImplemented, then b.__rmul__(a)). (I called dividing the integral numerator and the denominator (n/d) of a number, "normalization"; with divisor always positive, and only the numerator signed. You end up doing this basically after every operation, which makes arithmetic using such a the type surprisingly "slow". For Python, it doesn't matter much, because it is quite slow anyway.)
At the bottom of the list, for Show quick moderation as select Checkboxes.
The rabbit hole went so far for me that I looked into the possibility of having the object file format support the definition (down to which register a parameter is passed in, as well as its type) of each parameter. At some point it gets too far, as in requiring completely new file formats for the toolchain to work on, and not just a completely new toolchain... ::)If the concept is workable, then difficulty of implementing it shouldn't be a barrier. A toolchain is written once (conceptually) but used millions of times. If it saves one millionth of the time it takes to create it, bonus. I may have mentioned (if not, I thought it) that letting the compiler decide just how a function/subroutine gets incorporated into final code (i.e pass a value, use a pointer, put it in a register, incorporate the code inline rather than as a callable (at the binary level) module, would improve code efficiency, and the source just gives preferences as to how it happens - fastest speed, minimum code, don't care.
Goodbye cruel thread!;D
It is possible for ELF object files by encoding the parameter type information in the version string. How practical that would be, I dunno; haven't tried it in real life.[...] having the object file format support the definition (down to which register a parameter is passed in, as well as its type) of each parameter.If the concept is workable, then difficulty of implementing it shouldn't be a barrier.
On a CPU like ATmega328P there are 32 registers, the C compiler (that I used) avoids using some registers because the libraries that may be incorporated use some of them by convention. But if the compiler was able to know which library components are used, and those components said something like"I need 3 registers but don't care which" then the compiler could optimise its use of registers.This is exactly why I use extended asm (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) when compiling C or C++ static inline functions with GCC, Clang, or Intel CC (using adding the __attribute__((always_inline)) function attribute, to force inlining of the function). Instead of specifying the exact registers, you use numbers (%0, %1) or names (%[foo], %[bar]), and use constraints (https://gcc.gnu.org/onlinedocs/gcc/Constraints.html#Constraints) to specify which kind of register can be used for the inlined function.
Then there's another variant of the declare statement using "as":
One of the things that is messy in assembler is using fractions in fixed point arithmetic. Floating point is fine when it is part of the instruction set, but not every microprocessor has FP.
ATmega328P there are 32 registers
I had not seen the term interval arithmetic before although I think I know the concept well. Maybe a tool?
@jportici
Scala?
One of the things that is messy in assembler is using fractions in fixed point arithmetic. Floating point is fine when it is part of the instruction set, but not every microprocessor has FP.
So being able to define something as (in an arbitrary language) binary [8\8] would be nice, an 8 bit integer part and an 8 bit fraction part. Or [0\24], [16\16] etc. The code required is simple, just shift bits or add zero bytes before computation. But interpreting what goes on is not so clear. Being able to express it in the language would be helpful.
Is this already a thing, or is it too obscure to be worried about? Languages I've encountered assume int is integer (funny about that) and if fractions are thing, it is done in floating point with a floating point library if not native to the architecture.
dcl rate fixed bin(15,8);
rate = 1101.0011101:b;
dcl counter; // defaults to fixed decimal 5 digits
declare rate float binary(32);
dcl size bin(15); // defaults to fixed
@jportici
Scala?
No, Basic 4 Android :D
A huge project that provides a common language framework to write apps for Android, iOS, PC (java).
Free to use for android/pc, paid updates for iOS but actually really cheap compared to other options.
The biggest plus, to me, is how it handles layouts. It's actually easy and the result is predictable and there is none of that declarative bullshit app programmers seem to be fond of these days (kotlin, flutter, looking at you.)
The fact that the language itself is a dialect of basic also means that it's actually understandable by people. It's not trying to reinvent the wheel by changing names to things, or strange cryptic syntax.
Community is great, libraries provided by the community actually work, and if they are popular they are adopted by the project creator, it's not the clusterfuck of cobbled together libraries like you find in flutter (ble library in flutter is too basic. An user threw up a library to give more functionality, the github has 700+ issues and most of the replies are go to this fork of the library, which has 500+ issues. I am terrified by that environment.)
Tutorial are always up to date, when something big changes in the language, like start with a template or with an empty application, or when a class becomes an internal library, the tutorial is rewritten with both approaches. You can't dream of better support
type baud_rates enum bit(8);
first = 1010 0011:b;
second = 1110 1101:b;
third = 1101 0111:b;
fourth = 0010 1100:b;
end;
type Outer structure;
cpu_id bin(31);
Inner structure;
cpu_mode bin(31);
cpu_rev bin(15);
large as Header;
Flag_table structure;
root pointer;
depth fixed bin(15);
end;
label string(64);
end;
owner string(64);
speed as baud_rates;
counter string(32);
end;
I had not seen the term interval arithmetic before although I think I know the concept well. Maybe a tool?
Good for fuzzy-logic, so, good for uc-robotics.
(just, it's 90s old-school, but still good)
Then there's another variant of the declare statement using "as":
RE: the As keywork, one programming language i use extensively have recently introduced explicit casting.
Since this language is derived from java, it follows the "everything is an object" rule. To cast one object you add .As(Type) after the object name, then you can use all the methods associated with the type
I had not seen the term interval arithmetic before although I think I know the concept well. Maybe a tool?
Good for fuzzy-logic, so, good for uc-robotics.
(just, it's 90s old-school, but still good)
It's good for many things. :)
And math concepts are never old school.
There is at least one language that seems to have many of the facilities the OP believes will be useful. It is contempory with his "favoured" language PL/1.One of the things that is messy in assembler is using fractions in fixed point arithmetic. Floating point is fine when it is part of the instruction set, but not every microprocessor has FP.
Fixed point was implemented in an HLL in the mid 60s....
There are three types of number, floating-point, fixed-point and integer. Except in certain part-word table-elements (see Section 4.4.2.2, “Part-Word Table Elements” [18]), all three types are signed. Numeric type is indicated by the word FLOATING or INTEGER or by the word FIXED followed by the scaling constants which must be given numerically, e.g.
FIXED(13,5) This specifies five fractional bits and a minimum of 13 bits to represent the number as a whole, including the fractional bits and sign. The number of fractional bits may be negative, zero, or positive, and may cause the binary point to fall outside the significant field of the number.
The same language also has something rather nice for bit manipulations...
Part-Words ... the desired field is selected by a prefix of the form BITS[ Totalbits , Bitposition] in front of the primary to be operated upon. The result of this operation is a positive integer value of width Totalbits and in units of the bit at Bitposition.
BITS[2,6]x := 3 has the effect of placing the binary digits 11 in bits 7 and 6 of the word allocated to x
Packed Data ... and one in which the data format is declared in advance. In the latter system, with which this section is concerned, the format is replicated to form a table. A group of n words is arbitrarily partitioned into bit-fields (with no fields crossing a word boundary), and the same partitioning is applied to as many such groups (m say) as are required. The total data-space for a table is thus nm words. Each group is known as a table-entry. The fields are named, so that a combination of field identifier and entry index selects data from all or part of one computer word, known as a table-element. The elements in a table may occupy overlapping fields, and need not together fill all the available space in the entry.
...
Part-Word Table Elements Elements which occupy fields narrower than a computer word (and only such elements) are declared in forms such as
rain UNSIGNED(4, 2) 2,0;
humidity UNSIGNED(6,6) 2,8;
temperature (10,2) 2,14;
It also has macros, for those that like such abominations...
A compiler embodies a macro processor, which may be regarded as a self-contained routine which processes the text of the program before passing it on to the compiler proper. Its function is to enable the programmer to define and to use convenient macro names, in the form of identifiers, to stand in place of cumbersome or obscure portions of text, typically code statements.
The language's definition is online at http://www.xgc-tek.com/manuals/pdf/xgc-c66-1.7-rm.pdf (http://www.xgc-tek.com/manuals/pdf/xgc-c66-1.7-rm.pdf)
So from a programmer's perspective, how does this IPL thing handle event/interupt bindings? Is there a notion of asynchronous threading - maybe across DMA boundaries?
In other words, does IPL facilitate a callback or declarative closure pattern that handles events from outside of the main thread/loop/queue?
Re: No love for CORAL 66?
It turned out the FORTRAN compiler on the VAX was actually quite clever.
Unfortunately, their CORAL-66 compiler thought it was clever too.
The first professional job I ever did was to help track down a bug in a sonar simulation system, written in CORAL-66 using the MASCOT methodology (defence bods will remember that). It always eventually crashed with a stack overflow, though the amount of run-time before the crash varied considerably.
Turned out that inside a double, nested FOR-loop was an IF statement of the form IF <x> AND <y> THEN... , where x and y were complex expressions.
Of course, if x turns out to be false, you don't have to evaluate y. So the optimising compiler added a jump to the next thing, which happened to be outside of the nested loops. But it left the temporary result for x on the stack. So eventually there would be an overflow.
The number of iterations of the loops and the value of x depended on the input data, but also on how much time the scheduler gave to each of the "concurrent" processes in its simulation of a real-time system. So even if you fed it the same input data, it would run for different times before crashing.
I remember going to a DECUS meeting to tell everyone about this. But it turned out that we were the last to know :(
namespace system.kernel private;
// Everything in here can only be accessed by code that is also inside the "namespace system.kernel private" namespace.
end;
namespace system.kernel;
// Code here is not private
end;
As part of the rationalization of the language, I've replaced "auto" / "automatic" with "stack", were all grown ups, time we stopped using these arcane terms.
As part of the rationalization of the language, I've replaced "auto" / "automatic" with "stack", were all grown ups, time we stopped using these arcane terms.
Hardly anyone uses 'auto' in C, and in C++11 and later it is a placeholder type specifier, meaning that the declared variable derives its type from the initializer (e.g. 'auto f = 3.0;' declares variable 'f' with type 'double').
Here's some actual real life CORAL 66 (http://www.gtoal.com/languages/coral66/restore.cor) source, it's very odd that the language seems to need single quotes around keywords, never seen that before, I wonder why they did that...Another way to differentiate between keywords and other identifiers to allow new keywords to be added later without causing problems?
Some compilers forced programmers to put all keywords in quotes, which was permitted by the language definition.
Here's some actual real life CORAL 66 (http://www.gtoal.com/languages/coral66/restore.cor) source, it's very odd that the language seems to need single quotes around keywords, never seen that before, I wonder why they did that...Another way to differentiate between keywords and other identifiers to allow new keywords to be added later without causing problems?
I've seen this in some Algol 60 programs, also discussed here:
http://cs.ecs.baylor.edu/~maurer/SieveE/Algol60.htm (http://cs.ecs.baylor.edu/~maurer/SieveE/Algol60.htm)QuoteSome compilers forced programmers to put all keywords in quotes, which was permitted by the language definition.
As part of the rationalization of the language, I've replaced "auto" / "automatic" with "stack", were all grown ups, time we stopped using these arcane terms.
Hardly anyone uses 'auto' in C, and in C++11 and later it is a placeholder type specifier, meaning that the declared variable derives its type from the initializer (e.g. 'auto f = 3.0;' declares variable 'f' with type 'double').
Good Lord! really? I guess it's so hard to add new keywords to the language that it's now become necessary to give the same keyword different meanings in different contexts!
the language has a name "IPL", the grammar is now very solid and stable and arbitrarily complex - legal - source code can be parsed reliably and a parse tree created ...This is the sort of thing that drove me crazy back in my college "compilers" class.
...
I'm stepping back from this work for a time now...
Quotethe language has a name "IPL", the grammar is now very solid and stable and arbitrarily complex - legal - source code can be parsed reliably and a parse tree created ...This is the sort of thing that drove me crazy back in my college "compilers" class.
...
I'm stepping back from this work for a time now...
I learned a hell of a lot about lexical analysis, parsing, and grammars, but the class essentially STOPPED and hand-waved away the actual code-generation (including any optimization steps that might or might not occur.) At the time I was a heavy-duty assembly-language programmer, and most of what I cared about was whether a compiler could come close to duplicating the efficiency of code I could write in assembly.
So I used to think that C owed its success and longevity to the small footprint of the require "runtime environment." But now I want to add that "code generation" for C code is "obvious." You can look at C code and picture what the code generated ought to look like. Perhaps the compiler optimizes that weirdly, perhaps not. Maybe this is what people OUGHT to mean when they say "C is just a high level assembler."
As languages get more complex, the code generation gets considerably LESS obvious.
I always figured that the failure of PL/1, even with IBM behind it, was that the compiler ended up being too complicated to port (in a meaningful way) to new architectures. I don't know if that was run-time environment related, code generation related, compiler size related, or some combination.
So - how is IPL doing in the code generation department? What's the first target?
This is how 99% of "I want to make my own compiler" threads end up being. Grammar is like 1% of the work and the easiest part at that.
I can sort of understand this for the education though. Code gen past the primitive stack machine with AST traversal and direct generation is really hard work, you would have to dedicate multiple semesters to that, and nobody has time for that.
And a lot of this work is not purely algorithmic as academics like, but more practical. There is no real theories, you just need to sit down and code. And the code gets messy and hard. This is the part where most new languages give up.
But now I want to add that "code generation" for C code is "obvious." You can look at C code and picture what the code generated ought to look like. Perhaps the compiler optimizes that weirdly, perhaps not. Maybe this is what people OUGHT to mean when they say "C is just a high level assembler."I agree.
This is how 99% of "I want to make my own compiler" threads end up being. Grammar is like 1% of the work and the easiest part at that.
With your never ending put downs and defeatism, this comes as no surprise. But please support the bizarre unscientific claim that the grammar design is 1% of the effort in designing a programming language? do you have any evidence? any data?
I want to recommend a great (if a little dated) book on compilers. It was the best of five books I used in the 90s for compiler implementation and explained the code generation far better than any of the other books.
...
These were all good books but the Bornat book showed the author's passion for the subject, he knew his stuff (it contains one if the very few, earliest discussions of coroutines).
My focus the past few months has been (to the irritation of some here) "the grammar" and that's because too few programming languages really, truly design the grammar. They seem (and this is true of the vast majority) to adopt some existing language (usually C, Java, Javascript) and then hang on the coat tails.
The problem here though is that a grammar is like clay, once it dries and the shape is formed, it is what it is and there's no going back. What I've been doing the past few months is like a potter at his wheel getting interesting forms but always keeping the clay wet, not letting it set just yet.
This is how 99% of "I want to make my own compiler" threads end up being. Grammar is like 1% of the work and the easiest part at that.
I can sort of understand this for the education though. Code gen past the primitive stack machine with AST traversal and direct generation is really hard work, you would have to dedicate multiple semesters to that, and nobody has time for that.
And a lot of this work is not purely algorithmic as academics like, but more practical. There is no real theories, you just need to sit down and code. And the code gets messy and hard. This is the part where most new languages give up.
QuoteMy focus the past few months has been (to the irritation of some here) "the grammar" and that's because too few programming languages really, truly design the grammar. They seem (and this is true of the vast majority) to adopt some existing language (usually C, Java, Javascript) and then hang on the coat tails.
The problem here though is that a grammar is like clay, once it dries and the shape is formed, it is what it is and there's no going back. What I've been doing the past few months is like a potter at his wheel getting interesting forms but always keeping the clay wet, not letting it set just yet.
I agree.
For the final project on my compilers course at the uni I wanted to deliver something that I wouldn't be too embarrassed about.
So definitely not a simple stack machine, but real register allocation with SSA, I also recall some rescheduling instruction to reduce the amount of data values alive at a time, constant propagation, elimination of pointless arithmetic (think x+0), dead code, tail call optimizations...
The humble goal was to write simple, recursive implementations of factorial, fibonacci and similar things and have them compiled down to a few lines of clean assembly.
This took me three weeks, several hours per day. My conclusion was that compilers can be done, but they are not exactly trivial :D
edit
That being said, it is my understanding that nowadays one can save a lot of time by writing a frontend for LLVM/GCC. These are supposed to have a lot of the hard stuff and all the low level code generation handled for you. Although when it comes to portability, I seem to recall that some of their CPU abstractions are still somewhat leaky...
I don't think anybody here has ever designed a grammar yet the prattlers here glibly dismiss its significance.
This is likely because the beginner books they've likely read never talk about it, they almost always use an existing language as their grammar.
I don't think anybody here has ever designed a grammar yet the prattlers here glibly dismiss its significance.
Probably, and probably not. The latter because it is a solved, and more importantly because other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)
QuoteThis is likely because the beginner books they've likely read never talk about it, they almost always use an existing language as their grammar.
Possibly.
If in order to complete a task you have to do either A or B, then start with the easier.
If in order to complete a task you have to do either A and B, then start with the more difficult.
You have to define the concepts/behaviour and grammar, and do the compilation and optimisation and code generation. You are starting with the the easiest. Shame.
This is how 99% of "I want to make my own compiler" threads end up being. Grammar is like 1% of the work and the easiest part at that.
With your never ending put downs and defeatism, this comes as no surprise. But please support the bizarre unscientific claim that the grammar design is 1% of the effort in designing a programming language? do you have any evidence? any data?
We will soon* have yet another data point. Now that you have finished your perfect grammar within two months of forum shitposting and ignoring all the experts, and had a bit of experimentation with off-the-shelf lexical tools, maybe a few hours of actual work, let's see how long it takes for you to have actual code generation which works for non-trivial pieces of code. Let's say, something you can compile the compiler itself with. Any single actual CPU architecture, maybe x86, or maybe ARMV7-M or RISCV. You should also have some kind of standard library with basic IO functionality, even for a single OS (POSIX or Windows).
*) or maybe not so soon. I'm not holding my breath
Then we can estimate the percentage that went into the design of grammar.
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)One thousand times this.
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)It took many years for C to have a defined sequencing and memory model, and to tackle aliasing in an optimization friendly way (I'm not expert enough to say whether they did it 100% correctly).
Hints to the OP on these topic have been mostly ignored. I'm not interested in grammar that much (though I of course have my personal taste).
I don't think anybody here has ever designed a grammar yet the prattlers here glibly dismiss its significance.
Probably, and probably not. The latter because it is a solved, and more importantly because other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)
So that's not a yes then is it? have you ever designed a programming language? have you ever designed anything? I'm assuming the answer is "No".
It isn't solved, a design isn't done until the goals of the design are met, until the objectives defined for the grammar met, until then it is not "done", do you really not understand what design is?
If in order to complete a task you have to do either A or B, then start with the easier.
If in order to complete a task you have to do either A and B, then start with the more difficult.
This is proof positive that you're a troll now. The order of phases in a project is not dictated by their relative complexity, one cannot build a rocket and send men to the moon without doing other stuff first like deciding on a route, deciding the likely fuel consumption, safety of the crew, costs, support staffing, team roles etc etc.
You have to define the concepts/behaviour and grammar, and do the compilation and optimisation and code generation. You are starting with the the easiest. Shame.
Your trolling is nothing to do with language design at all, it really about your naive "understanding" of project planning and scheduling, you think you are an expert yet your own buffoonery above shows us otherwise.
There are a handful of people in this thread who know how to discuss this subject like adults, you are clearly not one of them.
I happen to have decades of experience as a software project manager, I've earned very large bonuses for delivering multi million dollar projects ahead of schedule, managing risk, maintaining progress, you have nothing to teach me about project management or programming language design.
Now please, pretty please with a cherry on top, stop trolling and let the adults here talk in peace.
Now please, pretty please with a cherry on top, stop trolling and let the adults here talk in peace.
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)It took many years for C to have a defined sequencing and memory model, and to tackle aliasing in an optimization friendly way (I'm not expert enough to say whether they did it 100% correctly).
Hints to the OP on these topic have been mostly ignored. I'm not interested in grammar that much (though I of course have my personal taste).
You speak of "hints", instead of hinting, state your view, be specific, what exactly do you want to suggest to me? I've not intentionally ignored suggestions that are on-topic, if it seems I have, then I apologize.
I've not intentionally ignored suggestions that are on-topic
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)One thousand times this.
It took many years for C to have a defined sequencing and memory model, and to tackle aliasing in an optimisation friendly way (I'm not expert enough to say whether they did it 100% correctly).
Hints to the OP on these topic have been mostly ignored. I'm not interested in grammar that much (though I of course have my personal taste).
BTW, my master thesis was, in fact, a compiler for occam, so the grammar was a given - not an issue*.
Being on MS-DOS, the major problem was rather the implementation of concurrency (PAR construct) and channels.
I went for the simplest solution: stack based VM, only minor optimisations, tick based multitasking; I wrote it in Turbo Pascal, but 80% of the runtime library was in assembler.
The generated code was also able to run on a NETBIOS network**, by just adding a configuration file matching channels and procedure with a named machine.
I had the pleasure of it being used in several technical schools to teach some programming concepts.
*And having being exposed to occam, Python's 'indentation is scope' just does not strike me as odd.
**That was the first time I encountered C as the bindings and library for NETBIOS networking available to me were C only. Did not think much of it at the time, but that was before C89 was finalized.
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)It took many years for C to have a defined sequencing and memory model, and to tackle aliasing in an optimization friendly way (I'm not expert enough to say whether they did it 100% correctly).
Hints to the OP on these topic have been mostly ignored. I'm not interested in grammar that much (though I of course have my personal taste).
You speak of "hints", instead of hinting, state your view, be specific, what exactly do you want to suggest to me? I've not intentionally ignored suggestions that are on-topic, if it seems I have, then I apologize.
Apology accepted.
Start with memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation, multithread parallelism, multicore operation, real-time guarantees... There are others scattered through the thread by several authors; try reading them.
other issues are far more problematic. (Examples: memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation)It took many years for C to have a defined sequencing and memory model, and to tackle aliasing in an optimization friendly way (I'm not expert enough to say whether they did it 100% correctly).
Hints to the OP on these topic have been mostly ignored. I'm not interested in grammar that much (though I of course have my personal taste).
You speak of "hints", instead of hinting, state your view, be specific, what exactly do you want to suggest to me? I've not intentionally ignored suggestions that are on-topic, if it seems I have, then I apologize.
Apology accepted.
Start with memory models, possibility of aliasing, fundamental concepts embodied in the language, optimisation, multithread parallelism, multicore operation, real-time guarantees... There are others scattered through the thread by several authors; try reading them.
Yes, you did only mention these, what of them? are you insinuating that something specific be incorporated? because there's nothing specific in what you wrote.
These are just headings for God's sake, no substance, what do you mean "memory models" so what EXACTLY would you like to see in a language for this? If these kind of vague subject headings is really all you have then there's not a lot I can do in any practical sense is there? Shall I say "OK I'll include a memory model" or "OK I'll make sure there's an optimizer"? I mean really? would that satisfy you?
You're trolling, pretending to contribute because you can't actually contribute, just endless posting of inane vacuities, coupled with an exaggerated sense of self importance, so sorry buddy I will be ignoring you from now on.
I suspect you don't understand why a memory model is necessary, let alone what is involved. Hint: Java had one from the beginning, but had to replace it when the new memory systems became available. Even experts find it difficult. C didn't have one until recently, after Hans Boehm forced people to understand what couldn't be done without one. Even experts didn't understand that!
So tell me, what is a "memory model" for, what purpose does it serve? what would happen without one? how many types are there? lots of questions there for you, lets see if you can answer any.
So tell me, what is a "memory model" for, what purpose does it serve? what would happen without one? how many types are there? lots of questions there for you, lets see if you can answer any.
[url=https://en.wikipedia.org/wiki/Memory_model_(programming)]https://en.wikipedia.org/wiki/Memory_model_(programming) (https://en.wikipedia.org/wiki/Memory_model_(programming))[/url]
So tell me, what is a "memory model" for, what purpose does it serve? what would happen without one? how many types are there? lots of questions there for you, lets see if you can answer any.
[url=https://en.wikipedia.org/wiki/Memory_model_(programming)]https://en.wikipedia.org/wiki/Memory_model_(programming) (https://en.wikipedia.org/wiki/Memory_model_(programming))[/url]
No surely you mean:
http://www.cs.cornell.edu/courses/cs2022/2011sp/lectures/lect06.pdf (http://www.cs.cornell.edu/courses/cs2022/2011sp/lectures/lect06.pdf)
or perhaps
https://livebook.manning.com/book/modern-c/chapter-12/ (https://livebook.manning.com/book/modern-c/chapter-12/)
or perhaps
https://www.cs.cmu.edu/~15122/handouts/20-cmem.pdf (https://www.cs.cmu.edu/~15122/handouts/20-cmem.pdf)
or even
https://people.cs.pitt.edu/~xianeizhang/notes/cpp11_mem.html (https://people.cs.pitt.edu/~xianeizhang/notes/cpp11_mem.html)
But that latter one is C++ not C...
Please show me what the C language standard actually says about a "memory model" after all it now "has" one, it got it "recently".
The Java Memory Model was the first attempt to provide a comprehensive threading memory model for a popular programming language.[2] After it was established that threads could not be implemented safely as a library without placing certain restrictions on the implementation and, in particular, that the C and C++ standards (C99 and C++03) lacked necessary restrictions,[3][4] the C++ threading subcommittee set to work on suitable memory model; in 2005, they submitted C working document n1131[5] to get the C Committee on board with their efforts. The final revision of the proposed memory model, C++ n2429,[6] was accepted into the C++ draft standard at the October 2007 meeting in Kona.[7] The memory model was then included in the next C++ and C standards, C++11 and C11.[8][9]
So tell me, what is a "memory model" for, what purpose does it serve? what would happen without one? how many types are there? lots of questions there for you, lets see if you can answer any.
[url=https://en.wikipedia.org/wiki/Memory_model_(programming)]https://en.wikipedia.org/wiki/Memory_model_(programming) (https://en.wikipedia.org/wiki/Memory_model_(programming))[/url]
No surely you mean:
http://www.cs.cornell.edu/courses/cs2022/2011sp/lectures/lect06.pdf (http://www.cs.cornell.edu/courses/cs2022/2011sp/lectures/lect06.pdf)
or perhaps
https://livebook.manning.com/book/modern-c/chapter-12/ (https://livebook.manning.com/book/modern-c/chapter-12/)
or perhaps
https://www.cs.cmu.edu/~15122/handouts/20-cmem.pdf (https://www.cs.cmu.edu/~15122/handouts/20-cmem.pdf)
or even
https://people.cs.pitt.edu/~xianeizhang/notes/cpp11_mem.html (https://people.cs.pitt.edu/~xianeizhang/notes/cpp11_mem.html)
But that latter one is C++ not C...
Please show me what the C language standard actually says about a "memory model" after all it now "has" one, it got it "recently".
I think everybody here means the ordering of memory fetch and store operations, as described in the Wikipedia article and in your last link. Yes, your last link is for C++, but for C the semantics are similar. For C, it was introduced in C11. See Wikipedia article.Quote from: Wikipedia articleThe Java Memory Model was the first attempt to provide a comprehensive threading memory model for a popular programming language.[2] After it was established that threads could not be implemented safely as a library without placing certain restrictions on the implementation and, in particular, that the C and C++ standards (C99 and C++03) lacked necessary restrictions,[3][4] the C++ threading subcommittee set to work on suitable memory model; in 2005, they submitted C working document n1131[5] to get the C Committee on board with their efforts. The final revision of the proposed memory model, C++ n2429,[6] was accepted into the C++ draft standard at the October 2007 meeting in Kona.[7] The memory model was then included in the next C++ and C standards, C++11 and C11.[8][9]
specification for CISO/IEC 9899:2011 5.1.2.3, 5.1.2.4
So tell me, what is a "memory model" for, what purpose does it serve? what would happen without one? how many types are there? lots of questions there for you, lets see if you can answer any.
[url=https://en.wikipedia.org/wiki/Memory_model_(programming)]https://en.wikipedia.org/wiki/Memory_model_(programming) (https://en.wikipedia.org/wiki/Memory_model_(programming))[/url]
No surely you mean:
... <waste of his life omitted>
Please show me what the C language standard actually says about a "memory model" after all it now "has" one, it got it "recently".
I think everybody here means the ordering of memory fetch and store operations, as described in the Wikipedia article and in your last link. Yes, your last link is for C++, but for C the semantics are similar. For C, it was introduced in C11. See Wikipedia article.Quote from: Wikipedia articleThe Java Memory Model was the first attempt to provide a comprehensive threading memory model for a popular programming language.[2] After it was established that threads could not be implemented safely as a library without placing certain restrictions on the implementation and, in particular, that the C and C++ standards (C99 and C++03) lacked necessary restrictions,[3][4] the C++ threading subcommittee set to work on suitable memory model; in 2005, they submitted C working document n1131[5] to get the C Committee on board with their efforts. The final revision of the proposed memory model, C++ n2429,[6] was accepted into the C++ draft standard at the October 2007 meeting in Kona.[7] The memory model was then included in the next C++ and C standards, C++11 and C11.[8][9]
I'm leaving this thread due to excessive trolling and spamming, should anyone want to contribute suggestions or questions about this work please do so using Github issues/discussions (https://github.com/Steadsoft/imperium) which I'll respond to regularly.
The OP has a habit of not reading pointers given to him, apparently preferring to spend his time ranting and casting doubt on the competence of those that nudge him in a useful direction.
Quotespecification for CISO/IEC 9899:2011 5.1.2.3, 5.1.2.4
The OP has a habit of not reading pointers given to him, apparently preferring to spend his time ranting and casting doubt on the competence of those that nudge him in a useful direction.
The OP has a habit of not reading pointers given to him, apparently preferring to spend his time ranting and casting doubt on the competence of those that nudge him in a useful direction.Behind write-only mode, well an old saying goes.. after a while arrogance makes the blind :-//
I have nothing constructive to say or offer with respect to designing a new programming language so I'll say something derogatory about the poster because that's all I'm capable of anyway.
Quotespecification for CISO/IEC 9899:2011 5.1.2.3, 5.1.2.4
Right thanks. So if I were to say the design decisions pertaining to this must be deferred until code generation is looked into, would that put a stop to some of incessant prattling some in this thread have resorted to?
Quotespecification for CISO/IEC 9899:2011 5.1.2.3, 5.1.2.4
Right thanks. So if I were to say the design decisions pertaining to this must be deferred until code generation is looked into, would that put a stop to some of incessant prattling some in this thread have resorted to?
There's no point in debating syntax and grammar without a shared understanding of what the syntax and grammar constructs mean.
Humpty Dumpty, via Lewis Carroll, had something to say about that back in the mid 19th century.
I'm leaving this thread due to excessive trolling and spamming, should anyone want to contribute suggestions or questions about this work please do so using Github issues/discussions (https://github.com/Steadsoft/imperium) which I'll respond to regularly.
Are you truly of the opinion that these questions cannot be discussed here and now?
I'm leaving this thread due to excessive trolling and spamming, should anyone want to contribute suggestions or questions about this work please do so using Github issues/discussions (https://github.com/Steadsoft/imperium) which I'll respond to regularly.
Promises.
All those questions are trivial to answer and are not important at all. All of them can be changed once you have anything working at all. This the color of the bike shed discussion all over again.
Also, 2-byte strings for Unicode is a bad idea. Either use plain UTF-8 or UTF-32. There is no point in using 16-bit encodings, you will instantly be running into the limitation of that when you want to use emojis.
dcl user_name string (32,1); // 2nd arg optional and must be 1,2 or 4
dcl user_name string (32,2);
dcl user_name string (32,4);
I don't think anybody here has ever designed a grammar
Also, 2-byte strings for Unicode is a bad idea. Either use plain UTF-8 or UTF-32. There is no point in using 16-bit encodings, you will instantly be running into the limitation of that when you want to use emojis.
I'm leaving this thread due to excessive trolling and spamming, should anyone want to contribute suggestions or questions about this work please do so using Github issues/discussions (https://github.com/Steadsoft/imperium) which I'll respond to regularly.
Promises.
Car crash TV or border police TV programmes are addictive, aren't they :)
Was it you or someone else that was trying to find a way to ignore his thread ;)
UTF-8 is an encoding, a way of storing a Unicode string in a byte array or file. Some UTF-8 characters are two or three bytes long, but that is not how they are stored in memory in string variables.
I don't think anybody here has ever designed a grammar
Good lord, the arrogance. How the heck would you know?
Sadly, it is becoming more and more apparent that you don't know the first thing about either CPU instruction sets or programming languages, and you won't listen to those who do, so I'm out of this conversation.
I've designed everything from new machine code instructions (which have, incidentally, an internal grammar of the construction of their encoding, not to mention their assembly language form), to file and protocol formats, to yes extensions to programming languages. I haven't yet been arrogant enough to think I'm going to design a from-scratch programming language grammar that others will find preferable to something based on something they are already familiar with.
One thing I learned very long ago is that while a machine can readily parse LR(n) grammars it is very unwise to expect a human reader (especially one different to the writer) to do so, and it's best to stick to LL(1) grammars as much as possible.
I'm leaving this thread due to excessive trolling and spamming, should anyone want to contribute suggestions or questions about this work please do so using Github issues/discussions (https://github.com/Steadsoft/imperium) which I'll respond to regularly.
Promises.
Car crash TV or border police TV programmes are addictive, aren't they :)
Was it you or someone else that was trying to find a way to ignore his thread ;)
It was and I did.
However once the mechanism was discovered I am also using it to ignore uninteresting topics on my "Show unread posts since last visit" and the ignored topics list turns out to be limited to 100 topics. That limit had now been reached, after five days, and this topic rolled off the end of the list.
Most of the new topics naturally go quiescent within five days, and if I have to re-add this topic to the ignore list once every five days that is at least a considerable improvement.
I don't think anybody here has ever designed a grammar
Good lord, the arrogance. How the heck would you know?
I don't think anybody here has ever designed a grammar
Good lord, the arrogance. How the heck would you know?
I've been the subject of the OP's arrogance, ignorance and willy waving too. I didn't let I go unchallenged and the OP has avoided challenging my response.
One thing I learned very long ago is that while a machine can readily parse LR(n) grammars it is very unwise to expect a human reader (especially one different to the writer) to do so, and it's best to stick to LL(1) grammars as much as possible.
Good lord, the arrogance (did you really think C and C++ are LL(1) the most revered language in this thread ?? !!)
ISTR the 100 is a magic number required because the ignoring is expensive within the forum's database.
Fortunately I'm a speedreader, so the length of the unread posts list is something I can tolerate without mechanical assistance.
ISTR the 100 is a magic number required because the ignoring is expensive within the forum's database.
That's not something I'd ask the database to do. I'd fetch the ignore list and put it in an in-memory hash table, then check each fetched or about-to-be-displayed topic against the hash table.QuoteFortunately I'm a speedreader, so the length of the unread posts list is something I can tolerate without mechanical assistance.
No matter how quickly you can read and ignore, it's going to be 10,000 times slower than PHP / Python / Perl / Ruby / Javascript doing the same.
One advantage of not pruning the unread posts list is my favourite concept: serendipity. I also like the way the word rolls off the tongue. Maybe I'll get to visit the country too, Serendib.
Designing a new programming language is an act of arrogance? wow
I'm starting to wonder if it's a severe cognitive impairment in understanding what people are actually saying. Seems to read but also seems unable to understand properly :-//
Designing a new programming language is an act of arrogance? wow
I'm starting to wonder if it's a severe cognitive impairment in understanding what people are actually saying. Seems to read but also seems unable to understand properly :-//
Some of it could be due to the topics being subtle and involved, thus not suitable for speedreading nor for quick posts on a forum. But other parts don't fit that.
I'm sure psychologists have several names for the observed behaviour; none of it is unique and we've all seen similar things before.
...
The list of contributors to this thread who are not in the above list would I think be shorter. Some of them have flown under the radar a little while "thank"ing posts critical of OP by people in the above list. And one or two seem to have escaped by virtue of, I suspect, comments that went "whoosh!". One comment implicitly comparing posts on this thread to ChatGPT output springs to mind, Mr SW :-)
I'm having similar issues on reddit at the moment with someone who was using a pseudonym (and account created specifically to pick a fight with me, it seems), and who turns out to be the leader of a well known and respected academic programming group. He's around 65 (not much older than me), but based on his apparent comprehension problems and his position, blog posts, and other information I strongly suspect he has done little or no technical work for many many years and is wholly occupied as a manager, fund/grant raiser (he's very proud of this area), and in general navigating public sector bureaucracies (I won't say which ones).
I don't think anybody here has ever designed a grammar yet the prattlers here glibly dismiss its significance.On a lighter note, I did design a variant of FORTRAN and called it CASHTRAN, a play on my surname. It was quickly pointed out this was an anagram of TRASHCAN. And there it remained.
There's a classic syndrome where, to employ all the clichés, a consultant doctor starts flying and then buys a high performance aircraft (especially a glider), and after not too long crashes it.
There's a classic syndrome where, to employ all the clichés, a consultant doctor starts flying and then buys a high performance aircraft (especially a glider), and after not too long crashes it.
A Bonanza is more traditional. Especially the V tailed ones (which were all there were when this cliche started).
If a glider then probably a Stemme, amirite? Or at least an 18m Ventus cM.
Very sad too when active 747 drivers die executing a botched circuit off a winch cable break.
All those questions are trivial to answer and are not important at all. All of them can be changed once you have anything working at all. This the color of the bike shed discussion all over again.
Also, 2-byte strings for Unicode is a bad idea. Either use plain UTF-8 or UTF-32. There is no point in using 16-bit encodings, you will instantly be running into the limitation of that when you want to use emojis.
dcl user_name string(32) ansi; // 1 byte chars, akin to a C char[]
dcl user_name string(32) bmp; // 2 byte chars any Unicode char in the range 0000–FFFF
dcl user_name string(32) ucs; // 4 byte chars any unicode char whatsoever in the range 000000-10FFFF
dcl user_name string(32) raw(8); // fixed length chars of 1 byte.
dcl user_name string(32) utf(16); // encoded variable length chars of 1 or 2 bytes.
raw(8), utf(8), raw(16), utf(16), utf(32) (being identical also raw(32) but we'd not bother to include that attribute).
I don't think anybody here has ever designed a grammar
Good lord, the arrogance. How the heck would you know?
I've been the subject of the OP's arrogance, ignorance and willy waving too. I didn't let I go unchallenged and the OP has avoided challenging my response.
But in that case don't make polls, start doing it.
Right now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
Mobbing, as a sociological term, means bullying of an individual by a group, in any context, such as a family, peer group, school, workplace, neighborhood, community, or online. When it occurs as physical and emotional abuse in the workplace, such as "ganging up" by co-workers, subordinates or superiors, to force someone out of the workplace through rumor, innuendo, intimidation, humiliation, discrediting, and isolation, it is also referred to as malicious, nonsexual, non-racial/racial, general harassment.
I started this thread hoping that it would be an informal, friendly sharing of ideas,
Anyway, if you have nothing to contribute then of course, you can stop discussing this subject.
I'm a very experienced software developer and very experienced with C
I don't think anybody here has ever designed a grammar
Good lord, the arrogance. How the heck would you know?
I've been the subject of the OP's arrogance, ignorance and willy waving too. I didn't let I go unchallenged and the OP has avoided challenging my response.
I think some facts are in order here.
I started this thread hoping that it would be an informal, friendly sharing of ideas, experienced engineering minded people who had ideas and enthusiasm for the concept of a new programming language that could deliver things that C is poor or weak at, a language that could be designed from the outset with MCU related needs in mind too.
Because a programming language and compiler are not for the beginner, because they are rather large projects, broad in scope and depth, I explained that I was no novice and had in fact designed and built a functioning compiler for a large language and I'd written that compiler in C, I pointed readers at Github where that codebase is available to anyone to look at.
From the outset, from the very first response to my opening post, there was discouragement, disdain and contempt at the mere suggestion of doing this.
After me making just two posts (this (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536722/#msg4536722)and this (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536830/#msg4536830)) this was what people were posting:QuoteBut in that case don't make polls, start doing it.
andQuoteRight now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
Note how discussing ideas, soliciting opinions, gathering potential requirements and considering features is regarded by him as "not actually doing anything"! The thread, the attempt to discuss IS DOING SOMETHING.
That frankly, set the tone for the thread, put downs, insults, dismissal and character assassination, I was made to feel unwelcome after making just two posts; go and read those posts and reflect on what I say.
If I've insulted anyone, if I've posted bluntly sometimes then recognize it is a reaction to the rudeness and insults that started less than ONE HOUR after I started the thread.
If you think I'm wasting my time, if you think I'm an idiot, if you think there's no prospect of this ever going anywhere then fine, you have that right, but all I ask is you leave the thread, don't post insults and inflammatory comments and whine and complain, just go away, leave me in peace to discuss a subject I'm passionate about with the few people here who have been polite and considerate and helpful - please.
I started this thread hoping that it would be an informal, friendly sharing of ideas,
Yet already one of your first replies, to the first helpful comments, from ataradov, one of the most helpful forum members who I have never seen fighting with anyone or discouraging any useful idea, was this:QuoteAnyway, if you have nothing to contribute then of course, you can stop discussing this subject.
When combined with the initial premise:QuoteI'm a very experienced software developer and very experienced with C
this didn't play well. Rinse and repeat - the rest is history.
The fact you can't deal with anyone here is quite revealing. Maybe take a good look in the mirror?
Right now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
my 14th post
I started this thread hoping that it would be an informal, friendly sharing of ideas,
Yet already one of your first replies, to the first helpful comments, from ataradov, one of the most helpful forum members who I have never seen fighting with anyone or discouraging any useful idea, was this:QuoteAnyway, if you have nothing to contribute then of course, you can stop discussing this subject.
When combined with the initial premise:QuoteI'm a very experienced software developer and very experienced with C
this didn't play well. Rinse and repeat - the rest is history.
The fact you can't deal with anyone here is quite revealing. Maybe take a good look in the mirror?
What you say is untrue, the facts prove it, it is another insinuation with slimy words like "one of your first replies, to the first helpful comments, from ataradov" a blatant mischaracterization of the truth.
Yes I did post that, but when? I'll tell you it was in my 14th post AFTER I'd been insulted with this in the 6th post.QuoteRight now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
That is not a "helpful comment" and you do not speak the truth.
Sijwasta didn't make that comment about scopes; ataradov did.
It would be better if you didn't mis-attribute statements.
my 14th post
It is interesting that even posts made by others are "your posts".
But I totally get it. Your thread, your rules, other people are yours, too. YOU'REFIREDIGNORED!!!1 and so on. :popcorn:
And all ataradov did was to rectify the false ideas about the importance and mechanisms of NOP insertion. Completely helpful.
Yet despite your false claims I actually did make the 14th post
my 14th post
It is interesting that even posts made by others are "your posts".
But I totally get it. Your thread, your rules, other people are yours, too. YOU'REFIREDIGNORED!!!1 and so on. :popcorn:
And all ataradov did was to rectify the false ideas about the importance and mechanisms of NOP insertion. Completely helpful.
Yet despite your false claims I actually did make the 14th post Siwastaja here it is. (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536938/#msg4536938)
The insult that led to me to make the 14th post came before, in the 6th post (less than one hour after me starting the thread) here it is (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536851/#msg4536851).
Yet despite your false claims I actually did make the 14th post
Oh, but your claim was that it was your 14th post, which is only true under the premise that all posts in a thread started by you are "yours".
In actual real world where others do not belong to you, post #14 was your 6th post (including the opening), or your 5th reply.
Or then again, there is the possibility of, to quote Nominal Animal, me failing English.
Yet despite your false claims I actually did make the 14th post
Oh, but your claim was that it was your 14th post, which is only true under the premise that all posts in a thread started by you are "yours".
In actual real world where others do not belong to you, post #14 was your 6th post (including the opening), or your 5th reply.
Or then again, there is the possibility of, to quote Nominal Animal, me failing English.
I can totally feel the importance of this discussion!
Nah. It is yet another comprehension failure - but not on your part.
my 14th post
It is interesting that even posts made by others are "your posts".
But I totally get it. Your thread, your rules, other people are yours, too. YOU'REFIREDIGNORED!!!1 and so on. :popcorn:
And all ataradov did was to rectify the false ideas about the importance and mechanisms of NOP insertion. Completely helpful.
Yet despite your false claims I actually did make the 14th post Siwastaja here it is. (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536938/#msg4536938)
The insult that led to me to make the 14th post came before, in the 6th post (less than one hour after me starting the thread) here it is (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536851/#msg4536851).
Ataradov's post was a calm statement of his tentative opinion. You may not have liked his opinion, but that is a different issue.
You have a tendency, in this thread at least, of being too ready to interpret a statement of a differing opinion as being an insult. (Hint: that's an opinion, not an insult!)
This is plain stupid
don't make polls, start doing it and if you have something useful implemented, we can discuss.
Right now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
I mean, someone like ataradov writes like that all the freaking time and I'm serious when I'm saying I don't remember it ever causing a misunderstanding before. You're the first I'm seeing having a problem with it, after many years hanging here.Many were triggered but they weren't brave enough to speak up against this horrible abuse.
my 14th post
It is interesting that even posts made by others are "your posts".
But I totally get it. Your thread, your rules, other people are yours, too. YOU'REFIREDIGNORED!!!1 and so on. :popcorn:
And all ataradov did was to rectify the false ideas about the importance and mechanisms of NOP insertion. Completely helpful.
Yet despite your false claims I actually did make the 14th post Siwastaja here it is. (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536938/#msg4536938)
The insult that led to me to make the 14th post came before, in the 6th post (less than one hour after me starting the thread) here it is (https://www.eevblog.com/forum/programming/a-new-hardware-oriented-programming-language/msg4536851/#msg4536851).
Ataradov's post was a calm statement of his tentative opinion. You may not have liked his opinion, but that is a different issue.
You have a tendency, in this thread at least, of being too ready to interpret a statement of a differing opinion as being an insult. (Hint: that's an opinion, not an insult!)
He saidQuoteThis is plain stupid
andQuotedon't make polls, start doing it and if you have something useful implemented, we can discuss.
andQuoteRight now this has a vibe of "making your own high performance oscilloscope" threads from 10 years ago. Those went for pages and pages with people daydreaming about it and not actually doing anything.
I have no idea whether he was calm or not, I made no suggestion he was not calm. The post and its tone was an insult, here's the test would a reasonable professional person speak that way to a coworker in a team setting in front of others? in front of say a HR staff member?
I really shouldn't have to even explain this, its just so basic that I'm dumbfounded at your support for it.
Don't you see the difference in saying:
This [doing X] is stupid[, so don't waste your time doing it]
vs.
The person saying this is stupid
?
Anyway, I'm done with off topic posts and insults, carry on if you must but it's not going to deter me from continuing these discussion with the few individuals that have earned my respect.
Giving five values:Code: [Select]raw(8), utf(8), raw(16), utf(16), utf(32) (being identical also raw(32) but we'd not bother to include that attribute).
So raw strings have every character represented by the same size unit, easy to index, iterate the strings and utf strings are encoded less wasted space but more costly to iterate and search. Simply assigning one to another would implicitly convert the string format too.