https://github.com/breckinloggins/erlisp
I'm currently working on writing one in Haskell using the Scheme48 tutorial [1], though I'd eventually like to replace the evaluator with an F-Algebra[2] so I can learn about that.
https://github.com/breckinloggins/scheme48
As a side note, would any Haskellers be willing to look at the abstraction I did for parsing #-forms? I created an adhoc data structure:
hashLiteralInfo = [
('f', (Nothing, \_ -> Bool False)),
('t', (Nothing, \_ -> Bool True)),
('b', (Just $ many $ oneOf "01", Number . toInteger . fromBase 2)),
('o', (Just $ many $ oneOf "01234567", Number . toInteger . fromBase 8)),
('d', (Just $ many $ oneOf "0123456789", Number . toInteger . fromBase 10)),
('h', (Just $ many $ oneOf "0123456789abcdefABCDEF", Number . toInteger . fromBase 16))
]
And then the parser consumes that: parseHashLiteral :: Parser LispVal
parseHashLiteral = do
char '#'
c <- oneOf $ map fst hashLiteralInfo
let literalInfo = lookup c hashLiteralInfo
case literalInfo of
Nothing -> fail "Internal parse error: unregistered literal info"
Just (Nothing, f) -> return $ f "unneeded" -- I know there's a more idiomatic way to do that
Just (Just literalParser, f) -> do
digits <- literalParser
return $ f digits
This works and is "abstracted", but doesn't feel idiomatic. Thoughts?[1] http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_H...
[2] http://bartoszmilewski.com/2013/06/10/understanding-f-algebr...
hashLiteralInfo =
[ ('f', pure $ Bool False)
, ('t', pure $ Bool True)
, ('b', fmap (Number . toInteger . fromBase 2) (many $ oneOf "01"))
-- ... and so on
]
The Boolean literal cases are then just parsers which consume no input and always return the same LispVal, instead of being separate, special cases that need to be handled by awkwardly passing a dummy string to a function which ignores its argument.https://github.com/breckinloggins/scheme48/commit/60d7930b4c...
In particular, with hashLiteralInfo setup as [(Char, Parser LispVal)], the parseHashLiteral function became as simple as:
parseHashLiteral :: Parser LispVal
parseHashLiteral = do
char '#'
c <- oneOf $ map fst hashLiteralInfo
case lookup c hashLiteralInfo of
Just x -> x
Nothing -> fail "Internal parse error: unregistered literal info" data HashLiteral a = HashLiteral { _character :: Char, _trailingChars :: [Char], _representation :: [Char] -> a}
(Underscores for lenses)
And then ``hashLiteralInfo :: [HashLiteral a]`` rather than an ad-hoc thing.Then you could do something like
binHashLiteral = HashLiteral { _char = 'b', _parser = Just . many . oneOf "01", _representation = Number . toInteger . fromBase 2 }
which has a nicer type (HashLiteral Number) than some weird nested tuple.However, if I decide to go forward with some of the more advanced things in the tutorial like threading good error messages and such, I'm pretty sure some new data types are in my future.
Also, when you say "underscore for lenses", did you mean that your example would be pretty clean with the Lenses library? I've been looking for an excuse to learn that so if so I'd love to learn more!
Also, now you have to write your own lookup function instead of just using Prelude.lookup.
(pun intended)
http://michaux.ca/articles/scheme-from-scratch-introduction
I had fun following along with him. Now, if he (or I can get my shot at it finished) can ever finish working on the byte-code based version!
The book is in two parts. The first starts from a simple evaluation function and enriches it with multiple name spaces, continuations and side-effects with commented variants, while at the same time the language used to define these features is reduced to a simple lambda-calculus.
The second part focuses more on implementation techniques and discusses precompilation for fast interpretation: threaded code or bytecode; compilation towards C. Some extensions are also described such as dynamic evaluation, reflection, macros and objects.
I would also find very interesting to see a step-by-step series on compiling scheme (with macros included) using llvm. I never tried really hard to look for one, though.
I should finish my Ada version sometime... Or just start a new project haha
(Last time I tried the latter the more vocal members of the user base complained so much the entire effort died. That was in 2000.)
Lisp implementation from one of the julialang devs
btw, not related to OP, those guys who think it is so clever to translate some primitive subset of Scheme into Haskell should appreciate that writing Scheme interpreter in Scheme is a part of CS61A course and it takes one page of much more readable code.
If, for some reason, you wish to write something other than toys, look at Mark Freeley, to realize that serious Scheme system is hard.
And does anyone else dislike web design that (1) limits the text to a column a third of the page wide, and then (2) because this is too small, requires a horizontal scrollbar on code segments?
For an explanation of a small Lisp interpreter (in Python) that uses sane web design, one might consider Norvig's http://norvig.com/lispy.html
I agree this page should be wider for posts like this.
Edit: Ok, ok. Now that the title of submission has been changed from "Toy Lisp Interpreter" to "Little Lisp Interpreter" my quip, having lost context doesn't make me smile anymore either.