8 | OpenBracket | CloseBracket
9 | OpenBrace | CloseBrace
10 | OpenParen | CloseParen
13 | Tilde | SpliceUnquote
22 let tokenize (str
: string) =
25 let inline isWhiteSpace
ch = ch = ',' || Char.IsWhiteSpace(ch)
26 let inline isNotNewline
ch = ch <> '\r' && ch <> '\n'
27 let inline isDigit
ch = Char.IsDigit(ch)
28 let inline isTokenChar
ch =
30 | '[' | ']' | '{' | '}' | '(' | ')'
31 | '\'' | '"' | '`' | ',' | ';' -> false
32 | ch when Char.IsWhiteSpace(ch) -> false
35 let rec skipWhile
pred p
=
37 elif
pred (str
.[p
]) then p
+ 1 |> skipWhile
pred
40 let rec accumulateWhile
pred (f
: string -> Token) start
p =
41 if p >= len then str
.Substring(start
, p - start
) |> f
, p
42 elif
pred (str
.[p]) then p + 1 |> accumulateWhile
pred f start
43 else str.Substring(start
, p - start
) |> f
, p
45 let accumulateString p =
46 let b = System.Text.StringBuilder()
47 let rec accChar
(ch : char
) n
=
48 b.Append(ch) |> ignore
52 if p >= len then raise
<| Error.expectedXButEOF
"'\"'"
54 | '\\' -> accEscaped
n
59 if p >= len then raise
<| Error.expectedXButEOF
"char"
61 | 't' -> accChar
'\t' n
62 | 'b' -> accChar
'\b' n
63 | 'n' -> accChar
'\n' n
64 | 'r' -> accChar
'\r' n
65 | 'f' -> accChar
'\f' n
66 | '\'' -> accChar
'\'' n
67 | '"' -> accChar
'"' n
68 | '\\' -> accChar
'\\' n
69 | _ -> raise
<| Error.expectedXButEOF
"valid escape char"
71 String(b.ToString()), n
73 let accumulateKeyword p =
75 if p >= len then raise
<| Error.expectedXButEOF
"keyword"
76 elif isTokenChar
str.[p] then accumulateWhile
isTokenChar Keyword p n
77 else raise <| Error.expectedX
"keyword char"
79 let accumulateSpliceUnquote p =
80 if p >= len then Tilde, p
81 elif
str.[p] = '@' then SpliceUnquote, (p + 1)
90 | ch when isWhiteSpace
ch -> getToken
n
91 | ';' -> skipWhile
isNotNewline n |> getToken
92 | '[' -> OpenBracket, n
93 | ']' -> CloseBracket, n
95 | '}' -> CloseBrace, n
97 | ')' -> CloseParen, n
98 | '\'' -> SingleQuote, n
100 | '~' -> accumulateSpliceUnquote n
103 | '"' -> accumulateString n
104 | ':' -> accumulateKeyword n
105 | '-' when isDigit
str.[n] -> accumulateWhile
isDigit Number p n
106 | ch when isDigit ch -> accumulateWhile
isDigit Number p n
107 | ch when isTokenChar ch -> accumulateWhile
isTokenChar Token p n
108 | _ -> raise <| Error.unexpectedChar
()
110 let rec accumulate
acc p =
111 match getToken
p with
112 | EOF, p -> List.rev
acc
113 | tok, p -> accumulate
(tok::acc) p