1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
type message = { prefix : string option; command : string; params : string list }
let msg ?prefix (command : string) (params : string list) : message = { prefix; command; params }
let word (s : string) : string * string =
match String.index_opt s ' ' with
| None -> (s, "")
| Some i ->
let rest = String.sub s (i + 1) (String.length s - i - 1) in
let rec skip r = if String.length r > 0 && r.[0] = ' ' then skip (String.sub r 1 (String.length r - 1)) else r in
(String.sub s 0 i, skip rest)
let parse (line : string) : (message, string) result =
let line =
let n = String.length line in
if n >= 2 && String.sub line (n - 2) 2 = "\r\n" then String.sub line 0 (n - 2)
else if n >= 1 && line.[n - 1] = '\n' then String.sub line 0 (n - 1)
else line
in
if String.length line > 510 then Error "a line longer than 512 bytes"
else
let prefix, rest =
if String.length line > 0 && line.[0] = ':' then
let p, rest = word (String.sub line 1 (String.length line - 1)) in
(Some p, rest)
else (None, line)
in
let command, rest = word rest in
if command = "" then Error (Printf.sprintf "no command in %S" line)
else
let rec params rest acc =
if rest = "" then List.rev acc
else if rest.[0] = ':' || List.length acc = 14 then
List.rev ((if rest.[0] = ':' then String.sub rest 1 (String.length rest - 1) else rest) :: acc)
else
let p, rest = word rest in
params rest (p :: acc)
in
Ok { prefix; command = String.uppercase_ascii command; params = params rest [] }
let print (m : message) : string =
let rec params = function
| [] -> []
| [ last ] when last = "" || String.contains last ' ' || last.[0] = ':' -> [ ":" ^ last ]
| p :: rest -> p :: params rest
in
String.concat " " ((match m.prefix with Some p -> [ ":" ^ p ] | None -> []) @ (m.command :: params m.params))
let nick_of (prefix : string) : string = match String.index_opt prefix '!' with Some i -> String.sub prefix 0 i | None -> prefix