standalone exapunks vm in ocaml
1(* This Source Code Form is subject to the terms of the Mozilla Public
2 License, v. 2.0. If a copy of the MPL was not distributed with this
3 file, You can obtain one at https://mozilla.org/MPL/2.0/. *)
4
5open MParser
6
7(* TYPES *)
8
9module StrSet = Set.Make (String)
10
11type parsed_host = string * int
12
13and parsed_link = string * int * string
14
15and parsed_file = {
16 name : int;
17 loc : string;
18 contents : Value.t list;
19}
20
21and parsed_hardware_reg = {
22 name : string;
23 loc : string;
24}
25
26and parsed_f_objs =
27 | File of parsed_file
28 | Hard of parsed_hardware_reg
29
30and parsed_environment = {
31 name : string option;
32 hosts : parsed_host list;
33 links : parsed_link list;
34 files : parsed_f_objs list;
35}
36
37and user_state = StrSet.t
38
39(* PARSERS *)
40
41let string_return str result = skip_string str >>$ result
42
43let open_square = skip_string "[" << spaces
44
45let close_square = spaces >> skip_string "]"
46
47let open_curly = skip_string "{" << spaces
48
49let close_curly = spaces >> skip_string "}"
50
51let p_name = many1_chars alphanum
52
53let host =
54 spaces >> p_name >>= fun name ->
55 get_user_state >>= fun s ->
56 if StrSet.mem name s then fail "Can't define two hosts with same name"
57 else
58 update_user_state (fun s -> StrSet.add name s)
59 >> spaces
60 >> between open_square close_square digit
61 >>= fun size ->
62 spaces >> string ";" >> spaces >>$ (name, int_of_string (String.make 1 size))
63
64let hosts = string "hosts" >> spaces1 >> between open_curly close_curly (many1 host)
65
66let number =
67 option (string "-") >>= fun neg ->
68 many1_chars digit >>= fun num ->
69 let num = int_of_string num in
70 return (if Option.is_some neg then -num else num)
71
72let p_link_int = between open_square close_square number
73
74let p_link_both =
75 p_name >>= fun host1 ->
76 spaces >> p_link_int >>= fun link1 ->
77 spaces1 >> skip_string "<->" >> spaces1 >> p_link_int >>= fun link2 ->
78 spaces >> p_name |>> fun host2 -> [ (host1, link1, host2); (host2, link2, host1) ]
79
80let p_link_forward =
81 p_name >>= fun host1 ->
82 spaces1 >> p_link_int >>= fun link1 ->
83 spaces1 >> skip_string "->"
84 >> (spaces1 >> p_name |>> fun host2 -> [ (host1, link1, host2) ])
85
86let p_link_backward =
87 p_name >>= fun host1 ->
88 spaces1 >> skip_string "<-" >> spaces1 >> p_link_int >>= fun link2 ->
89 spaces1 >> p_name |>> fun host2 -> [ (host2, link2, host1) ]
90
91let p_link =
92 choice [ p_link_both; p_link_forward; p_link_backward ] << spaces << skip_string ";"
93
94let p_links =
95 skip_string "links" >> spaces1
96 >> between open_curly close_curly
97 (many1 (spaces >> p_link << spaces) |>> fun links -> List.flatten links)
98 << spaces
99
100let p_file_contents =
101 between open_square close_square
102 ( spaces
103 >> sep_end_by
104 (choice [ number |>> Value.int; p_name |>> Value.key ])
105 (string "," << spaces)
106 << spaces
107 |>> fun contents -> contents )
108
109let p_file =
110 number >>= fun name ->
111 spaces >> between open_square close_square p_name >>= fun loc ->
112 get_user_state >>= fun s ->
113 let loc_name = string_of_int name ^ loc in
114 if StrSet.mem loc_name s then fail "Can't define hardware register twice in one host"
115 else
116 update_user_state (fun s -> StrSet.add loc_name s)
117 >> spaces >> skip_string ":" >> spaces >> p_file_contents
118 >>= fun contents -> string ";" >> spaces >> return (File { name; loc; contents })
119
120let p_hardware_name =
121 string "#" >>= fun r ->
122 many1_chars alphanum |>> fun name -> r ^ name
123
124let p_hardware_register =
125 p_hardware_name >>= fun name ->
126 spaces >> between open_square close_square p_name >>= fun loc ->
127 get_user_state >>= fun s ->
128 let loc_name = name ^ loc in
129 if StrSet.mem loc_name s then fail "Can't define hardware register twice in one host"
130 else
131 update_user_state (fun s -> StrSet.add loc_name s)
132 >> string ";" >> spaces
133 >> return (Hard { name; loc })
134
135let p_file_alts = spaces >> choice [ p_file; p_hardware_register ]
136
137let p_files =
138 spaces >> skip_string "files" >> spaces1
139 >> between open_curly close_curly (many1 p_file_alts |>> fun files -> files)
140
141let environment : (parsed_environment, user_state) t =
142 spaces >> skip_string "environment" >> option (many1_chars letter) >>= fun name ->
143 spaces >> open_curly >> spaces >> option hosts << spaces >>= fun hosts ->
144 spaces >> option p_links >>= fun links ->
145 spaces >> option p_files >>= fun files ->
146 close_curly
147 >>
148 let hosts = Option.value ~default:[] hosts in
149 let links = Option.value ~default:[] links in
150 let files = Option.value ~default:[] files in
151 return { name; hosts; links; files }
152
153(* PRINTING FUNCTIONS *)
154
155let show_parsed_host ((name, size) : parsed_host) : string =
156 Printf.sprintf "%s [%i];" name size
157
158let show_parsed_link ((left, link, right) : parsed_link) : string =
159 Printf.sprintf "%s [%i] -> %s;" left link right
160
161let show_parsed_file file_obj : string =
162 match file_obj with
163 | File { name; loc; contents } ->
164 Printf.sprintf "%i [%s]: [ %s ];" name loc
165 (contents |> List.map Value.show |> String.concat ", ")
166 | Hard { name; loc } -> Printf.sprintf "%s [%s];" name loc
167
168let show_parsed_environment env =
169 let hosts =
170 env.hosts |> List.map (fun h -> " " ^ show_parsed_host h) |> String.concat "\n"
171 in
172 let links =
173 env.links |> List.map (fun h -> " " ^ show_parsed_link h) |> String.concat "\n"
174 in
175 let files =
176 env.files |> List.map (fun h -> " " ^ show_parsed_file h) |> String.concat "\n"
177 in
178 [
179 (*AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*)
180 Printf.sprintf "environment %s{"
181 (match env.name with
182 | None -> ""
183 | Some n -> n ^ " ");
184 (if List.is_empty env.hosts then "" else Printf.sprintf " hosts {\n%s\n }" hosts);
185 (if List.is_empty env.links then "" else Printf.sprintf " links {\n%s\n }" links);
186 (if List.is_empty env.files then "" else Printf.sprintf " files {\n%s\n }" files);
187 "}";
188 (*AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*)
189 ]
190 |> List.filter (fun line -> line != "")
191 |> String.concat "\n"