standalone exapunks vm in ocaml
at main 191 lines 6.0 kB view raw
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"