Samir Benmendil [Sun, 14 Jan 2018 00:24:21 +0000 (00:24 +0000)]
Handle boolean constants
Turns out that Scheme defines `#t` and `#f` to be `true` and `false`.
`let` is used to devife a new symbol `atom` which is a String.
`case...of` matches the literals and constructs a `LispVal` of the
appropriate type and value. `_` is a match all token.
Samir Benmendil [Sun, 14 Jan 2018 00:19:18 +0000 (00:19 +0000)]
Parse Atoms which start with a letter or symbol
`<|>` is a Parser combinator, the choice operator. It will try the first
parser, if it doesn't match anything, try the second parser, and so on.
`a:b` creates a list from `a` and `b`. Another possibility would be to
use `[a] ++ b`, where a new list with a single element is created `[a]`
and concatenated with `++`
Samir Benmendil [Sun, 14 Jan 2018 00:03:01 +0000 (00:03 +0000)]
Parse Strings surrounded by `"`
We need to use a `do-block` because we would like to keep the return
value of a parse in the middle. So we parse a single `char '"'`, parse
`many (noneOf "\"")` and assign the return value to `x` and parse
another `"`.
We apply the `String` constructor to the value of `x` to turn it into a
`LispVal` with `String x`.
Finally we inject the `LispVal` into the `Parser` monad. We need to do
this because each line of a `do-block` needs to be of the same type but
the result of `String x` is a `LispVal`, not a `Parser`. `return` wraps
the `LispVal` up in a `Parser` action that consumes no input.
Consider `a $ b c` as syntactic sugar for `a (b c)`.
Samir Benmendil [Sat, 13 Jan 2018 23:24:30 +0000 (23:24 +0000)]
Ignore spaces
We add the spaces `Parser` which returns nothing `()` and skips any
spaces.
`spaces` is already defined in Parsec, but does not do exactly what we
want, we can hide it from the import.
`skipMany a` matches 0 or more `a` and skips them.
`>>` is the **bind** operator. It may have completely different meanings
depending on the monad you use. In the Parser monad in means "try the
first parameter, then try the second with anything that is left", i.e.
"first skip all the spaces, then match the symbol".
Samir Benmendil [Sat, 13 Jan 2018 23:05:55 +0000 (23:05 +0000)]
readExpr function to check whether the input matches symbol
`readExpr :: String -> String` defines `readExpr` to be a function
(`->`) from String to a String.
`readExpr input = ...` will assign anything on the rhs to the `readExpr`
function where `input` is mapped to the first parameter of that
function.
`case ... of` is a *filter*.
`parse a b c` is a function from Parsec where `a` is the parser it should
use, `b` is the name of the input which is used for error messages and
`c` is the String to parse.
`parse` returns an `Either` type whose `Left` constructor indicates an
error. If we get a `Right` value, we bind it to `val`, ignore it and
return the String "Found Value".
Samir Benmendil [Sat, 13 Jan 2018 22:41:38 +0000 (22:41 +0000)]
Define a parser for various symbols in Scheme
`symbol :: Parser Char` is probably not needed (?)
I don't understand where the `Parser` type comes from, I cannot see it
in the doc of `Text.ParserCombinators.Parsec`. It may be that
`ParserCombinators` is a compatibility layer and that `Parser` is
defined somewhere else.
Samir Benmendil [Sat, 13 Jan 2018 22:22:28 +0000 (22:22 +0000)]
Add Parsec dependencie
This is a Parser module that will be used in the project.
`stack` makes it quite easy to add dependencies. Simply change the
`dependencies` list in `package.yaml` and run `stack build` again. Any
missing modules will be installed.
Samir Benmendil [Sat, 13 Jan 2018 22:06:26 +0000 (22:06 +0000)]
Print the sum of two arguments
`read`'s type is defined as `read :: (Read a) => String -> a`, i.e.
`read` takes as `String` and returns an `a` (number?). I don't know what
`=>` means. (`(Read a)` is an instance of class Read ?)
`show`'s type is a bit more complicated it seems as it has multiple
instances for various types of input. But in essence it takes a type and
returns a `String`.
Samir Benmendil [Sat, 13 Jan 2018 21:56:06 +0000 (21:56 +0000)]
Hello World with argument
This will simply print "Hello, " followed by the first argument from the
commandline.
Every Haskell program needs to have a main "action" in the Main module.
This is the entry point.
Files starting with `module <Foo>` will be part of the `Foo` module.
By convention modules are capitalized and definitions aren't.
`import System.Environment` provides `getArgs` to access cli arguments.
Haskell is strongly typed and types may be assigned with `::`. In this case
`main` is of type `IO ()` which is an IO action holding no information.
(more on that later hopefully, as I don't fully understand that.)