Nim compiler — Pascal source code
github.com
github.com
// This scanner is handwritten for efficiency. I used an elegant buffering
// scheme which I have not seen anywhere else:
// We guarantee that a whole line is in the buffer. Thus only when scanning
// the \n or \r character we have to check wether we need to read in the next
// chunk.
Turbo Pascal uses the same buffering scheme, seehttps://turbopascal.org/processing-source-line-characters
Turbo Pascal though needs the crlf within the first 128 bytes otherwise the compiler quits with an error „Line too long".
"Because of my interest in Pascal programming language and compilers I created a Turbo Pascal compiler written in Turbo Pascal - TPC16. This is a compiler compatible with the original Borland Turbo Pascal 7 command line compiler tpc.exe. Later I modified this compiler to compile under Delphi - TPC32. On the foundations of this compiler I later created Turbo51 - Pascal compiler for 8051 microcontrollers. Then I decided to publish the secrets of the first compiler I have created and this website was born. Here you can find the internals, algorithms and data structures of the Turbo Pascal 7 command-line compiler. You can also download the executable file of the TPC16 compiler.
I created the compiler from scratch, but credits for the beauty of the language and for the exceptional elegance of the compiler go to Niklaus Wirth, Anders Hejlsberg and Borland."
Barry also stated this 5 years ago, see
https://news.ycombinator.com/item?id=10202563
but that is not true.
How can I be so sure? I reverse-engineered pretty much all Turbo Pascal versions starting with v4.0 all the way up to v8.0 (Delphi v1.0). I even disassembled the beta versions of Delphi, v7.9h and v7.9k, to see and study the transition from Turbo Pascal to Delphi.
That being said, what you are looking at at turbopascal.org is an absolute mind-blowing reverse-engineering job. One hint is when you look for example at
Procedure AddReferenceRecordForTypedConstant (UnitSegment, BlockRecord: Word; ReferenceFlags: TReferenceFlagSet; DX, TypedConstantOffset: Word);
https://turbopascal.org/processing-typed-constants
at the bottom of the page - where the author couldn't come up with a proper name for the DX register, so he left that identifier unchanged. If you disassemble Turbo Pascal "tpc.exe" and look for that subroutine you'll see that Anders Hejlsberg really used the DX register.... (I know, not really convincing...)
Isn't original TP7 source code available?
The sources of TP6 have leaked a few years ago. Search for exmortis on HN.
But you are right in that it uses the same algorithms, structures and control flow as the real Turbo Pascal and therefore should be bug-for-bug compatible but is written in Pascal instead of assembly.
I'm still dreaming of a world where Rust had Nim syntax.
Rust has a bigger ecosystem of course, but Nim's is pretty decent and you can use C, C++ and JS libs natively for anything else.
Overall, I like it, but I could not justify using it in an enterprise setting without knowing that the direction of the language is going towards what is now the established minimum safety, which is borrow checking and no UB.
Zig might be a language you are interested in. Simple like C with sensible ways of dealing with UB and bounds checking, as well as options instead of Nulls.
Can you mention which part of the Rust language you found complicated (in real world), excluding the ownership/memory management area (which includes: bck, lifetimes, smart pointers, traits related to synchronization)?
I read a lot of comments about Rust's complexity, and while nobody doubts the complexity of the ownership/memory management, there's a very common lack of details when it comes to the rest of the language.
Nice.
Do you have more resources on that?
Also, does Nim have a WASM target?
https://nim-lang.org/blog/2020/10/15/introduction-to-arc-orc...
https://www.youtube.com/watch?v=aUJcYTnPWCg
https://github.com/stisa/nwasm
You can also go an Emscripten route: https://hookrace.net/blog/porting-nes-go-nim/
Re: borrow checking: https://nim-lang.org/docs/manual_experimental.html#view-type... It's still pretty early and also IME the mix of arc (which is determinstic) with manually managed memory (esp. through some data structure that manages entity data like entt) has been pretty nice.
Nim stops race conditions by controlling data passage among threads (disallowing shared data between threads, in general) and always has.
I am not familiar enough with Rust to say that everything rust can do and assure is indeed covered in Nim; however, plain Nim without using the escape hatches (which like Rust unsafe code, exist for those times you must use them) is safe.
Nim macros are a lot nicer than Rust's, but Rust's trait system is very powerful and expressive. Sure, you can do something like that with Nim's macros, but you can't expect that all third party code is going to play nice.
That's the problem with the macro approach: it's great because it's ultra-flexible, but it also sucks because it's ultra-flexible. Everybody ends up doing their own little DSL that fits their use case or their personal taste, and it adds friction at the interfaces.
Because having tried to explore these questions by studying Nim projects, going through a book and working on a game engine in it the past months; I've found that ufcs+overloading covers the static traits scenario, and it feels like the runtime traits object scenario is better self-rolled (I don't really want the compiler impl'ing vtables underneath me, save deciding to reuse a static dispatch feature for a dynamic one; if I want a struct of func ptrs I will make a struct of func ptrs.) at least for my usage. Various Nim code I come across is usually pretty readable, and I don't need to invent macros willy nilly.
ufcs feels more elegant than needing to decide whether to namespace a function inside a struct or not (or which one), which is what most langs (including Rust) seem to do.
Not sure what you mean there, there's product types with tuples and objects, and sum types as object variants.
> Pattern matching
Eh, I mean it's just sugar over a case statement.
let
num = 5
str = case num
of 1: "One!"
of 2, 3, 5, 7, 11: "This is a prime"
of 13..19: "A teen"
else: "Unknown" # Compile error if all cases arent covered.
As you say there's libraries that let you deconstruct and partially match more complex types if you need that. Being able to create things like pattern matching, async, and novel multithreading runtimes as a library in Nim shows how powerful (and useful) the metaprogramming is.Enums in Rust are more interesting, but again nothing you can't do with an object variant.
It's certainly cool and interesting to see that nim(rod) was written in pascal 11 years ago?
I love Pascal, I just give up on it too fast. But man it's fast, compiles fast, "easy" to use, until you start doing real world stuff.
Just like I used to call Windows DLLs or COM libraries from TPW and Delphi.
Which incidently is how Google pushes everyone to write native code on Android, as little as possible, just tiny native libraries. Even the NativeActivity is just a regular Java Activity implementation with predefined native methods declarations that the shared object is expected to fill in, to be properly called from the framework.
There are so many things that were better in its field, domain or niche but simple got washed out in History. It was only the other day someone commented Rails, despite its similarity in concept and execution, it is still not as good as good old Enterprise Objects Framework from NeXT.
http://www.kevra.org/TheBestOfNext/page570/page571/page573/p...
What they miss in regard to Delphi is proper AOT to native code capabilities, something that they should have supported (from my point of view) since version 1.0. Java had it via commercial JDKs (which only enterprises cared about), .NET via NGEN, mono aot and some research stuff like CosmOS, Singularity or Midori.
However now 20 years later they are finally realising that must be an option, but that is mostly thanks to the competion from Go, Rust, Swift and friends than anything else.
Interesting that you mention EOF, it eventually became WebObjects (in Java) and it was due to NeXT / Sun collaboration that J2EE was born. Many Java bashers don't realise how much influence Objective-C had in the Java ecosystem, in language features, language runtime, and yes J2EE.
Regarding things being washed out of history, check out on Burroughs, VMS, Solo Pascal, Mesa, Multics, PL.8 for examples of systems programming before C was at all relevant outside Bell Labs walls, the Xerox PARS Workstations namely Interlisp-D, Mesa, Mesa/Cedar, Smalltalk or Wirths linage of Modula-2 and Oberon derived work based on Xerox PARC work.
Unfortunely computer history isn't something that most computing degrees spend time on.
>What they miss in regard to Delphi is proper AOT to native code capabilities
May be I am just a fan of Pascal. Which for some reason most developers hate and opt for PL with more C like syntax.
>Unfortunely computer history isn't something that most computing degrees spend time on.
Yes. We end up not knowing the how and why it was like that in the first place. And ends up with people reinventing everything.
[1] https://www.quora.com/What-was-it-like-to-be-a-software-engi...