A programming language in 450 lines of JavaScript
jsfiddle.net
jsfiddle.net
But if you use the OCaml version with a recent version of OCaml, you'll run into a few issues. I sent in a patch with a few simple updates to the path mailing list earlier this year, but last I checked it wasn't merged.
Patch is here (it's a text file, despite the "bin" extension):
http://permalink.gmane.org/gmane.comp.compilers.llvm.cvs/183...
I also pasted the patch here:
It is available at his ETHZ site nowadays,
To be fair, though, the 450 lines include about 120 lines of comments, a configurable full operator precedence parser, syntax for array creation and indexing, range and looping operators, short-circuit logical operators...
Most minimal language implementations skimp on syntax because it is irrelevant to a language's expressive power. The point of this exercise, though, was to make one that didn't.
https://github.com/samphilipd/the-little-schemer/blob/master...
I wrote an implementation of Scheme in Javascript to use as I work through the excellent book 'The Little Schemer'. Turns out you can implement a basic version of the language in just 7 functions.
It's Turing complete and you can use the base methods to build any function you need.
EDIT: though I did end up writing a Pong game in it: https://github.com/capnmidnight/betty-boop/blob/master/pong....
The Fibonacci code example classically has a horrid runtime. Will like likely crash your browser when supplied a number higher than the low thirties. IIRC it has a O(2^n) runtime and can be improved either by a bottom up approach (non-recursive) or a "dynamic-programming" (OOohhhh, buzzwords) method where you use memoization (memo table) to store (or memoize) results.
Unless looping and counting is that much faster than doing some floating point math?
EDIT: Tied with JadeNB (https://news.ycombinator.com/item?id=8555051).
So close, and only ngorenflo (https://news.ycombinator.com/item?id=8555050) can come between us. :-)
In that spirit, one can adopt a sort of compromise notation: `O(phi^n) = o(2^n)` (where `=` should really be `\subseteq`).
EDIT: Tied with Mithrandir (https://news.ycombinator.com/item?id=8555049).
There are a few operators, with several levels of precedence, both prefix operators, infix notation, but no sign of post-fix notation like (!) for 3! == 6 and 5! == 120
There also seems to be list data type as well as strings and numbers.
let x! = if x == 0 then 1 else x * (x - 1)! end in 5! end
Any operator can be prefix, infix or postfix depending on the relative priority with the operators next to them. For instance, in "1 ^ * 2" the engine interprets ^ as postfix because ^ has higher priority (it just gets null as its right hand side). In "1 * ^ 2", ^ is prefix. Unless specified otherwise operators are given maximal priority, which is why the factorial example works.The engine just has specific provisions to allow operators in "prefix position" to have different priority than in infix position, otherwise prefix minus would fail to work as expected.
Would you mind pointing me in the right direction? Like a very brief(ie., a sentence or two), high-level overview of how you might approach the task?
http://jsfiddle.net/f1ycatas/2/
See line 266 for the syntax definition (the precedence entry for "->" on line 278 is also relevant) and line 409 for the implementation of pattern matching. I use a few auxiliary functions here: extractArgs just checks that an AST node has a certain form and extracts parts of it, and listify is a kind of normalization function that will give you a list of expressions or statements.
I think the code is relatively straightforward: we have a list of "pattern -> body" statements. For each entry we create a fresh environment for the variables declared by the pattern and we try to match the pattern with the value. For matching, if a pattern is a variable name we set that variable in the environment, if it's a number or string we check for equality, and if it's a list of subpatterns we check that we have a list of the correct length and we check the subpatterns recursively. If there's a match, we execute the corresponding body.
If that interests you, I wrote a compile-to-JS language called Earl Grey (http://breuleux.github.io/earl-grey/repl/) using a similar parser and it has rather sophisticated pattern matching. The implementation is quite a lot more than 450 lines, though :)
;)
"Have you heard about our Lord and Saviour JavaScript?"
"How to touch yourself at night without JavaScript knowing it."
"Breaking news! POP3 and IMAP written in Javascript!"
"How I ported the control software of a nuclear reactor to reactive Javascript"
"Linux kernel ported to JavaScript running in the browser. See how we did it."
"How I made a filesystem in javascript."
"How I got my girlfriend pregnant using JavaScript."
"How to avoid getting HIV using JavaScript."
"The Linux kernel doesn't have enough javascript."
"I recommended my boss to rewrite the local powergrid infrastructure to javascript and how I lost my job."
"I decided to re-implement Javascript in Javascript. It failed. Here is my story."
"ABC in # lines of JavaScript (400 comments, 1000 points)"
The article is also in the book Beautiful Code (O'Reilly)
Any inspiration from the Tiger book for the language?
Python is actually pretty neat for doing it and you even have tools like PyPy (with RPython) to write a JIT for the language if you wish.
Do you mean it because of the formal logic being used?
Using a regex for parsing a whole language is something I've never seen done, in 25+ years of writing and reading compilers as a hobby.
Using a regex for tokenization or parsing simple sub-expressions is sometimes done, but that too is fairly rare outside of toy parsers.
Doing the translation step through replace on the basis of a regex is also not going to work for anything but the simplest translations, not least because the resulting monstrous regex would be nearly impossible for a human to reason about.
But the thing that terrified me about it was the thought of someone lifting something lie that out of the browser sandbox and running it server side: The combination of a near undecipherable regex for parsing/translation coupled with eval() to run the result would be a near certain security nightmare.
The `eval()` part is also nothing new or strange. There are many systems which let you input expressions, compile it and run. Scala does this, as does Nim, as did Forth for a longest time.
In general compilation is not magic, on the contrary, it's conceptually simple and it's a good thing to know the basics. This is the view I wanted to express.
Then you seem to have misunderstood the point of my initial comment entirely, which is down to the specific case of suggesting regexes + eval() as a reasonable way to implement a compiler.
> because I assumed the OP meant this and not a single, giant regexp
The linked implementation already uses regexps for tokenization. OPs comment explicitly states "you could use replace() with a fat regex, and then eval()". I find it hard to interpret that as anything other than a suggestion to use a single, giant regexp.
> The `eval()` part is also nothing new or strange. There are many systems which let you input expressions, compile it and run.
Having an eval() is nothing "new or strange" but that was not what I was reacting to.
> In general compilation is not magic, on the contrary, it's conceptually simple and it's a good thing to know the basics. This is the view I wanted to express.
I've implemented several compilers and interpreters, and is writing a multiple-year-long article series on writing a Ruby compiler, so I agree (though Ruby is testing my patience...).
But personally dragging out regexps is the last thing I'd do for illustrating the conceptual simplicity of compilation... Personally, when I see people dragging out regexps, and they're longer than about 5 characters, I assume that's where I'll be most likely to find bugs.
Yeah, I agree, sorry about that.
> I find it hard to interpret that as anything other than a suggestion to use a single, giant regexp.
Now that I look at this you're probably right. I assumed any meaningful compilation is actually impossible with a single regexp and so tried to interpret original comment in a way which made at least some sense for me.
> But personally dragging out regexps is the last thing I'd do for illustrating the conceptual simplicity of compilation...
Well, there are some advantages to using regexps as an example: they are widely known and they also are able to describe regular languages. But I have no experience at all talking to people about compilation, so I'll assume that you're right and that using regexps makes it actually harder to explain things :)