mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 21:57:38 +03:00
- 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.
89 lines
3.4 KiB
Forth
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 []
|