1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-17 17:50:24 +03:00
mal/impls/fsharp/reader.fs
Joel Martin 8a19f60386 Move implementations into impls/ dir
- Reorder README to have implementation list after "learning tool"
  bullet.

- This also moves tests/ and libs/ into impls. It would be preferrable
  to have these directories at the top level.  However, this causes
  difficulties with the wasm implementations which need pre-open
  directories and have trouble with paths starting with "../../". So
  in lieu of that, symlink those directories to the top-level.

- Move the run_argv_test.sh script into the tests directory for
  general hygiene.
2020-02-10 23:50:16 -06:00

89 lines
3.4 KiB
Forth

module Reader
open System
open Tokenizer
open Types
open Node
type MutableList = System.Collections.Generic.List<Node>
let inline addToMutableList (lst:MutableList) item = lst.Add(item); lst
let quote = Symbol("quote")
let quasiquote = Symbol("quasiquote")
let unquote = Symbol("unquote")
let spliceUnquote = Symbol("splice-unquote")
let deref = Symbol("deref")
let withMeta = Symbol("with-meta")
let rec readForm = function
| OpenParen::rest -> readList [] rest
| OpenBracket::rest -> readVector (MutableList()) rest
| OpenBrace::rest -> readMap [] rest
| SingleQuote::rest -> wrapForm quote rest
| Backtick::rest -> wrapForm quasiquote rest
| Tilde::rest -> wrapForm unquote rest
| SpliceUnquote::rest -> wrapForm spliceUnquote rest
| At::rest -> wrapForm deref rest
| Caret::rest -> readMeta rest
| tokens -> readAtom tokens
and wrapForm node tokens =
match readForm tokens with
| Some(form), rest -> Some(makeList [node; form]), rest
| None, _ -> raise <| Error.expectedXButEOF "form"
and readList acc = function
| CloseParen::rest -> Some(acc |> List.rev |> makeList), rest
| [] -> raise <| Error.expectedXButEOF "')'"
| tokens ->
match readForm tokens with
| Some(form), rest -> readList (form::acc) rest
| None, _ -> raise <| Error.expectedXButEOF "')'"
and readVector acc = function
| CloseBracket::rest -> Some(acc.ToArray() |> Node.ofArray), rest
| [] -> raise <| Error.expectedXButEOF "']'"
| tokens ->
match readForm tokens with
| Some(form), rest -> readVector (addToMutableList acc form) rest
| None, _ -> raise <| Error.expectedXButEOF "']'"
and readMap acc = function
| CloseBrace::rest -> Some(acc |> List.rev |> Map.ofList |> makeMap), rest
| [] -> raise <| Error.expectedXButEOF "'}'"
| tokens ->
match readForm tokens with
| Some(key), rest ->
match readForm rest with
| Some(v), rest -> readMap ((key, v)::acc) rest
| None, _ -> raise <| Error.expectedXButEOF "'}'"
| None, _ -> raise <| Error.expectedXButEOF "'}'"
and readMeta = function
| OpenBrace::rest ->
let meta, rest = readMap [] rest
match readForm rest with
| Some(form), rest -> Some([withMeta; form; meta.Value] |> makeList), rest
| None, _ -> raise <| Error.expectedXButEOF "form"
| _ -> raise <| Error.expectedXButEOF "map"
and readAtom = function
| Token("nil")::rest -> Node.SomeNIL, rest
| Token("true")::rest -> Node.SomeTRUE, rest
| Token("false")::rest -> Node.SomeFALSE, rest
| Tokenizer.String(str)::rest -> Some(String(str)), rest
| Tokenizer.Keyword(kw)::rest -> Some(Keyword(kw)), rest
| Tokenizer.Number(num)::rest -> Some(Number(Int64.Parse(num))), rest
| Token(sym)::rest -> Some(Symbol(sym)), rest
| [] -> None, []
| _ -> raise <| Error.invalidToken ()
let rec readForms acc = function
| [] -> List.rev acc
| tokens ->
match readForm tokens with
| Some(form), rest -> readForms (form::acc) rest
| None, rest -> readForms acc rest
let read_str str =
tokenize str |> readForms []