Show HN: My Turbo Pascal compiler in JavaScript
teamten.com
teamten.com
I looked at parser (hand written are best), this part spotted my eyes https://github.com/lkesteloot/turbopascal/blob/master/Parser... - IMHO order of checking whatever node is identifier or not have to be reversed. Now, such simple program:
program Test;
procedure foo; begin end;
begin foo := 10; end.
produces error 'Error: can't cast from 70 to 76 ("10", line 8)' - whatever this means. :)
It was a pity that C took over the PC world as it did, Turbo Pascal could do everything better than it, including being safe by default, modules, OO.
Why Borland, why!
Turbo Pascal had all the capabilities of C as well. I can produce a one to one table, if you wish.
Additionally after version 5.5, it also had support for OO.
Why not use APL then?
Programs are to be read, not to be write-only.
You say that like reading comic books is an anti-intellectual crime.
Getting to the point, look at these examples and tell me which one is better from the readability perspective.
> for(std::vector<std::shared_ptr<MyClass> >::const_iterator it = v.begin(); it != v.end(); ++it)
or
> for(const auto &value : v)
Even in the case when the "value" variable's inferred type isn't apparent from the context, we can use
> for(const std::shared_ptr<MyClass> &value : v)
which is still much more readable than the monstrosity in the first example.
I completely agree with that. For example, static typing adds verbosity, but it also documents the program and makes figuring out a large codebase a lot easier. The "noise" can even be reduced in languages with type inference like D, Haskell or C++11.
The thing is, unnecessary verbosity only crowds the code and doesn't add any value. Most of the keywords in "if ... then begin ... end else begin ... end" serve no real purpose and could be replaced with a symbol, like a curly brace.
if
...
else
...
end
So it cuts down on the clutter vs. Pascal, but sticks to words rather than single character symbolsI used to be a strong believer in static typing and insist that dynamic typing would be extremely error prone, but when I actually tried it, I found that the additional testing required is pretty much non-existent - if you test properly, you tend to cover types as a side effect.
I'd still prefer static typing, but only if it doesn't get in the way. And that means minimal annotations, and minimal restrictions.
Later Pascal/Delphi compilers had very useful syntax highlighting built in, so this is really not an issue - unless you prefer as much code as possible crammed onto a single "page" (IMO not really helpful for reading).
{} are also somewhat harder (more strenuous) to type than begin/end, especially on german keyboards (it's Alt Gr + 7 / Alt Gr + 0) and even more so on german Apple keyboards ({} are not even printed on any keys there and they are on different key combinations than on PC keyboards).
> {} as delimiters make much more sense than BEGIN/END.
Sure {{{{{}}}}}}
That's MUUUUCH better!
Now we can discuss tab vs spaces.
PS: spaces obviously.
Yes, I did it on purpose.
> PS: spaces obviously.
Well there we happen to agree. :)
Now, after many private and commercial projects, I know that is about reading and understanding code. And about eliminating bugs. :)
With the exception of a few curmudgeons, C was the way to go for most people. You lost nested procedures, but the stuff you got back was immense. Pascal's big problem was that the extensions required to make it a "real" systems programming language (arbitrary memory access, I/O that didn't suck, etc.) were not standardized; good luck porting anything. Pascal strings were effectively broken (with a proliferation of Str255 / Str64 / Str32 types that were effectively incompatible unless you cheated).
C, while it still lacked a standard, had everything you needed out of the box, and the direction for a standard was clear.
Pascal became a legacy language and died at Apple in the early 90s.
I only moved into C land (actually C++), when I started developing to UNIX was well.
I still remember when after my first Xenix session, I asked my teacher about using Turbo Pascal and being disappointed for it not being available.
> C, while it still lacked a standard, had everything you needed out of the box, and the direction for a standard was clear.
Except modules, namespaces and being unsafe by default.
While in college, I got myself an oddball MS-DOS computer. My dad read an article in the business pages about this guy who had the audacity to market a complete software product for 39 bucks. Dad didn't know anything about computers, except that there was growing interest in programming, so he got Turbo Pascal version 1 for me as an Xmas present. That was the original version, and I stuck with Turbo Pascal through successive revisions until 1994 when I discovered HyperCard on an Apple Mac.
In my view, among the strengths of TP, its manuals shouldn't be overlooked. Each version came with a relatively slim yet complete manual that a person could actually read. Of course it helped that the target platform was relatively simple too.
I cut my programming teeth on Pascal and Turbo Pascal. This is hugely nostalgic for me!
At some point I made a GUI for DOS, similar to Windows Explorer (with a desktop, icons, start menu, context menus and obligatory clock! :) ).
I called it Winnie. Then my 386 died and I lost the code :/
Thanks for the memories!
Congratulations on your work and thanks for the nostalgia!
I started in Basic then moved to Turbo Pascal so I could compile my code & distribute it - mostly on 5.25" floppies as cover disks on magazines.
Later by uploading them to BBSes via a 9600bps modem (yes 9.6Kbps was super quick in those days)!
The equivalent of WordPress back then was Telegard which was written in Pascal. The sourcecode got loose which resulted in several spin-off "Telegard hacks" and many of the "elite" BBS where essentially themed versions of that original Telegard.
Then I heard Pascal morphed into Delphi? I never made it to Delphi. By 1994 I was into Javascript, Java, Rexx and best of all the opposite sex ;-)
So when I got to learn C, my reaction was like "meh", given that I was already used to Turbo Pascal capabilities in type safety, modules, OO, low level programming (everything that C does), blazing compilation times.
So I quickly moved away from C into C++ that allowed me to use stuff I was already used to from Turbo Pascal. Parallel to that I used most of Oberon family of languages.
I'm interested in studying the code and possibly replicating it for the purpose of learning about how compilers work. Could you give a general overview of the components, or steps you took to implement them? Thanks!
It's a two-pass compiler. The first pass runs the code through the Stream, Lexer, and Parser classes to generate a parse tree (with type information). The second pass generates bytecode using the Compiler class. The compiled bytecode is then handed to Machine to execute. Start in IDE.js in the _run() function. Also go into turbo.css and comment out the "display:none" line in the .debug-output block. That'll let you see the parse tree and generated bytecode.
Make that a feature!
Took me a good 30 minutes to remember how to write hello world in PASCAL :-) And I admit I forgot about the period on the End statement.
http://www.teamten.com/lawrence/projects/turbo_pascal_compil...
Well done!
http://www.joelonsoftware.com/articles/fog0000000023.html
I miss Joel's writing!
Turbo Pascal really mostly "only" does things the way Wirth used to: As simple as possible. Wirth's languages are all designed for single pass compilation with direct code generation, and if you write your compilers that way, they will be fast. It is very, very hard not to make them fast that way.
His compiler textsbooks, all the way back to "Compilerbay" in 1977 (using PL/0 as the language) included full source code for compilers following that style, and they're so small the entire compilers fit in a few handful pages.
(EDIT: just to make clear: I do admire Turbo Pascal - I used it quite a lot, but main the genius of Turbo Pascal was to pick the Wirth way of doing compilers and integrate it with a simple "IDE" - a large part of the amazing impression of Turbo Pascal was that you did not have to write your code to disk, exit the editor, run a compiler etc. but could do it all in one)
It was not a good benchmark. It was prevalent because it was trivial to port, not because it was good.
(Note that UCSD Pascal was based on a part of Wirths research groups "porting kit" - their intent was not for p-code to be used for production systems, but as an easy way of getting a Pascal compiler running on a new platform to be able to then use the target platform while retargeting the code generator)
> It had to be implemented contrary to beliefs common then: it was itself written in asm,
That was nothing out of the ordinary at the time, though not something Wirth himself did, as a large part of his mission was to teach the development of compilers.
But compilers for small systems were often written in asm at the time. It'd not be very viable to write compilers in high level languages for a C64 or other home computers, for example, yet there were plenty of compilers for small systems. When I wrote my first compilers on the C64 and a bit later on the Amiga, it never even occurred to me to write them in a high level language until a few years later (incidentally when I got HiSoft Pascal - an integrated Pascal environment for the Amiga very much in the spirit of Turbo Pascal)
> it was never just a compiler but the editor and compiler at once, kept everything in memory if it could, produced the pure native code processor specific and never produced obj files.
That's technically true, though the compiler that became Turbo Pascal was actually originally a separate, stand alone compiler.
Combining the compiler with the editor was fairly uncommon and certainly innovative. When Kahn at Borland decided he wanted an environment like that, though, he went out and bought an existing, standalone, commercial compiler, from Anders Hejlsberg, and brought him to Borland.
Producing pure native code on the other hand was not unusual - UCSD's p-System was an aberration in that respect, and it's greatest lasting legacy was in inspiring VM based systems; e.g. Bill Joy has cited it as one of the inspirations behind the JVM. Not producing object files was also quite common - for many small systems there was no tradition of object files at all, as they were too small for separate compilation to make much sense.
Pascal (and most of Niklaus Wirth's languages) was designed to be easy to compile. The grammars are very small (typically less than a page of text; Turbo Pascal used to include the full BNF grammer in the manual), and his languages are designed to be possible to compile in one pass.
Also "traditional" Wirth-style compilers usually output code directly rather than build an abstract syntax tree and a lot of compilers of the era were heavily influenced by his compiler textbook, which included the full source of a compiler.. They also tend to output object files or final executables directly rather than go via an assembler.
All of that combine to make it very easy to make the compilers very small, and very fast even on very slow machines. For an example of simplicty, take a look at the source code to an educational Oberon-0 (subset of his Oberon language) compiler, from one of his books: http://www.inf.ethz.ch/personal/wirth/books/CompilerConstruc... (it generates code for a virtual RISC machine, and one of the files includes an interpreter for the generated code)
The whole book is available as a PDF here: http://www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf ; Wirth's material on compiler contruction evolved over the years from an early book about writing a PL/0 compiler, through Pascal and Modula (2) and finally Oberon, but always focusing on simplicity.
For a time I wanted to go to ETH Zurich to study computer science there because of Wirth. I'm a bit of a Wirth fanboy (which is ironic, since my favourite language these days is Ruby - a language that is pretty much the anti-thesis of Wirth's focus on simplicity of implementation).
As as a final digression, lets play six (well, 3) degrees of Pascal to Javascript: One of Wirth's PhD students was Michael Franz who got his PhD with a one of my favourite dissertations, on dynamic architecture independent code generation for Oberon. Dr Michael Franz co-invented the trace-trees technique that went into Mozilla's TraceMonkey version of their javascript JIT with Andreas Gal. Andreas Gal now works at Mozilla with Brendan Eich, the creator of Javascript.
This compiler started out as a five-pass compiler, then I reduced it to two. I could have reduced it to one and simplified the code quite a bit, but having the intermediate parse tree made debugging easier. The parse and compile passes together take less than 20ms on the largest Pascal program I have (rose.pas).
He is most likely the reason why Go methods share Oberon-2's method declarations.
I keep meaning to go back and read more of their research output - it's amazing just how small and simple a lot of their systems were.
I keep looking forward to the day it becomes mainstream.
Sadly only people like us, that experimented with ETHZ's work seem to share such enlightenment.
-bowerbird