Re: Opinions On A New Programming Language
Posted: Wed Nov 22, 2017 12:43 pm
That looks very much like the old style of C declarations.
The Place to Start for Operating System Developers
https://f.osdev.org/
...~ wrote:That looks very much like the old style of C declarations.
Code: Select all
// my language
func main(argc: int, argv: **char) -> int {
...
}
// old style of c decl
main(argc, argv)
int argc;
char **argv;
{
}
Code: Select all
const char *const s = "hello world";
Code: Select all
const s: *const char = "hello world";
Code: Select all
const s: int16ptr -> const char = "hello world"
Code: Select all
const s = "hello world";
Code: Select all
var object: object;
Code: Select all
func fclose(FILE) {
...
}
~ wrote:It's a matter of making that assembly language easier to understand, simpler, more portable, like NASM syntax over plain Intel syntax over AT&T syntax.
~ wrote:I've managed the low level problem at least for x86 by making it possible to use the very same NASM assembly code portably to generate 16, 32 or 64-bit code with a header file called x86 Portable, which adds things like wideword, wideax, widebx, widecx, widedx, widesi, widedi, widesp, widebp, a16/a32/awide, o16/o32/owide.
~ wrote:I intend to use x86 Portable always for the code generated by my compiler.
~ wrote:In that way I can generate assembly code with portability at the same level to that of C
~ wrote:x86 Portable is just a NASM include header file that adds automatically-sized instructions and registers for word/dword/qword/wideword according to the target x86 CPU,
I already plan on doing that in the same way that the '-g' switch of the C/C++ compiler generates debugging information.~ wrote:You could make that your compiler generates debug/information files that store all user identifiers, data types, etc. It could work as a tool to list all functions, variables, data types, etc., in a program.
You can also generate an information file that shows how each custom data type was resolved from the top level until the point where a construct made only of basic data types is built so that knowing what a data type is really made of can be made trivial by the compiler.
C already has an automatically-sized type optimized for the target CPU's register width: int.~ wrote:About using automatically-sized variables for increased portability, if you think about it, it would be better to use something like intwide_t/uintwide_t, than having to decide between standard longs and ints.
C already has this too, though only from C99 onwards. The signed form is intptr_t, and the unsigned form is uintptr_t.~ wrote:ptrwide_t
That is exactly what int and long already are! The standard C numeric types are (IIRC) defined as:~ wrote:About using automatically-sized variables for increased portability, if you think about it, it would be better to use something like intwide_t/uintwide_t, than having to decide between standard longs and ints.
Well, gee, we can just use the type tags too... oh, wait, you tell me that the x86 doesn't support tagged memory, and neither do any of the other major general-purpose ISAs today? Shocking!~ wrote:With automatic size selection, you bring automatic register/word width portability to any CPU architecture, not just x86, and with that your code would be much cleaner.
I really don't know why types like ptrwide_t/uintwide_t/intwide_t were never added to the C/C++ standard and assembly language. Code today would be incredibly more portable now.
WUT?!?! Which x86 are you talking about? There is no such thing in x86! This is one of the most absurd statements you have ever made, and frankly, that's astounding by itself!~ wrote:Even when automatic word width selection is a very important programming concept used in x86,
Actually, those are now more the exception rather than the rule. Dynamic languages, both older ones like SNOBOL, Icon, Prolog, and almost the entire Lisp family, and newer ones like Python, Ruby, and Io (and newer Lisps such as Clojure), all use flexible numbers of one variety or another - since there is no express typing, the compiler or interpreter is free to select the appropriate size and representation, and resize it when something bigger or less precise is needed - often with some sort of arbitrary-precision data types (AKA BigInt, BigFixed, and BigFloat types) used when there is an overflow. They generally have a 'numeric tower', or a class hierarchy that serves the same purpose, and will generally go from~ wrote: it doesn't seem to be integrated anywhere else, not even in the most recent .NET languages, Java, or anywhere else. I will add those types to my compilers because I know that they alone can simplify the whole programming facilities of any language in existence.
Ah, thank you, I had a feeling I was missing things.Octocontrabass wrote:C already has this too, though only from C99 onwards. The signed form is intptr_t, and the unsigned form is uintptr_t.~ wrote:ptrwide_t
It's definitely better in terms of flexibility and simplicity. It's one of the things I love about languages like Lisp and Self (Smalltalk). The downside though is that you pay for the overhead of runtime compilation and dynamic binding. This specific language is intended for the same niche as C/C++, so that's a tradeoff I'm not willing to make.Schol-R-LEA wrote:That having been said, my point about dynamic type resolution might be of interest here, in light of your discussion earlier with Solar about virtual vs. non-virtual methods. My view is that, for polymorphism in both functions and types, it is something better bound as late as possible - possibly as late as runtime. The trick here is for the compiler to identify cases where polymorphism isn't necessary - cases where the type, or at least its size constraints, are known ahead of time so it can resolve them early and choose a fixed representation that is more efficient.
It sounds more or less like it's just an optimization for linked lists that happens to work because the lists in Lisp are value types (immutable). In my language, they're references by default like in C/C++, but can be made into value types by adding a $ sigil:Schol-R-LEA wrote:Maybe this is a Lisp-ish view, as non-toy Lisp compiler implementations are full of this sort of thing, and some are even used by (non-toy) interpreters. The highly dynamic approach inherent to Lisp makes them necessary for anything like acceptable performance. Even things as basic to the language as lists are often not what they seem to be - nearly ever serious Lisp interpreter since the days if the MIT AI Lab (circa 1965) has used 'CDR-coding' to snap the pointers for lists when the are created, turning the linked lists into tagged arrays of elements (which could be addresses to a datum or another list, or any tagged datum that would fit into the same space as an address, or in some cases more exotic things such as tagged indexes into blocks of homogeneous data elements which could be treated as a unit by the garbage collector) as a way of reducing both space and time overhead.
Code: Select all
func cons(item: int, list: $[int]) -> $[int] {
return [int] { item, list }
}
Yeah, as I mentioned before, it's certainly debatable. Being virtual never causes problems, but being non-virtual makes a system more rigid and hard to change. That being said, I also think it's important to base the design of the language on what we do empirically rather than a one-size-fits-all approach. It would get old rather quickly if I had to constantly sprinkle keywords all over the place to improve performance because the compiler is making assumptions that are rarely the case.Schol-R-LEA wrote:In my view, it would make sense to have virtual (or at least potentially virtual) be the default, and have some modifier indicate when something has to be resolved early. Similarly, a variable in a 'parent class', 'interface', 'template', or 'typeclass' could be resolved as the actual type/class if the compiler can determine early on that it can only be an object of a certain sub-class, even if the programmer can't be certain.
These are definitely on my workbench. I'm thinking long and hard about things like allowing the language to modify it's own syntax trees before evaluation/compilation (like Lisp does).Schol-R-LEA wrote:(And, since in my OS I mean to allow both load-time resolution and runtime code synthesis, it may vary between loads of the same program, or even over time as it runs in the case of some system-service-related objects - but that's getting ahead of things, as it isn't clear if it is really applicable to modern hardware at all.)
I certainly agree with the philosophy, but as I stated, I think the true path here is to design the language based on what the programmer is usually doing rather than trying to create a one-size-fits-all tool.Schol-R-LEA wrote:Basically, my goal in my own language(s) is to allow the programmer to make these decisions, but not to require them to do so if the aren't directly relevant to the programmer's needs - basically, allow optimization without forcing premature optimization. This is contrary to both the C/C++ school of thought, where the programmer has to make these decisions for all cases, but also to the Java/C# school, where such decisions are often taken out of their hands (while anomalously still requiring them to make some of them without a sensible default). It is a fine line to walk, and I will admit that it may not be possible, but I do mean to try.
Yeah. Thats actually why there's a "var" keyword in the languageSchol-R-LEA wrote:On the topic of type or class polymorphism, I was also wondering if you had considered going with the sort of type inference found in Haskell and Rust. This relates back to the previous assertion, in that it is essentially a way of exposing that mechanism for use by the developers of new types or classes
Code: Select all
var foo = "bar";
The language currently supports it in the sense of overloaded functions and UFCS (Unified Function Call Syntax) and I'm currently working on an implementation of constrained type substitution. Originally, I had planned a syntax like:Schol-R-LEA wrote:I was also wondering if you were familiar with predicate dispatch, and in particular Millstein's seminal paper on the topic, and whether you saw it as something that would fit in with your design.
Code: Select all
func add(x: $T, y: T) -> T;
Code: Select all
func strdup(str: %cstr) -> /cstr;