68 lines
1.8 KiB
OCaml
68 lines
1.8 KiB
OCaml
open Periodic
|
|
open Parser
|
|
|
|
type element =
|
|
{ symbol : string
|
|
; count : int
|
|
}
|
|
|
|
type molecule =
|
|
{ elements : element list
|
|
}
|
|
|
|
let unary_symbol_p : string parser =
|
|
let* x = upper in
|
|
String.make 1 x |> return
|
|
|
|
let binary_symbol_p : string parser =
|
|
let* x = upper in
|
|
let* x' = lower in
|
|
String.make 1 x ^ String.make 1 x' |> return
|
|
|
|
let symbol_p : string parser =
|
|
let* xs = binary_symbol_p <++ unary_symbol_p in
|
|
match StringMap.find_opt xs periodic_table with
|
|
| Some _ -> return xs
|
|
| None -> match element_find_closest xs with
|
|
| [] -> fail "invalid atomic element"
|
|
| xs -> List.fold_left (fun acc x -> acc ^ Printf.sprintf "%s " x) "" xs
|
|
|> Printf.sprintf "invalid atomic element. Did you mean any of the following: %s"
|
|
|> fail
|
|
|
|
let element_count_p : int parser =
|
|
let* x = many1 digit in
|
|
List.to_seq x
|
|
|> String.of_seq
|
|
|> int_of_string
|
|
|> return
|
|
|
|
let element_p : element parser =
|
|
let* s = symbol_p in
|
|
let* n = element_count_p <++ return 1 in
|
|
{ symbol = s
|
|
; count = n
|
|
} |> return
|
|
|
|
let molecule_p : molecule parser =
|
|
let* xs = manyTill element_p eof in
|
|
return { elements = xs }
|
|
|
|
let parse_molecule s : (molecule, error list) result =
|
|
match make_input s |> molecule_p.run with
|
|
| Ok [] -> failwith "TODO: make method total; empty list"
|
|
| Ok ((x,_)::_) -> Ok x
|
|
| Error xs -> Error xs
|
|
|
|
let print_molecule (s: molecule) : unit =
|
|
List.iter (fun x -> Printf.printf "%s, %i\n" x.symbol x.count) s.elements
|
|
|
|
let print_error (xs: error list) (s: string) : unit =
|
|
List.iter (fun x -> Printf.printf "%s\n%*s\n%s\n\n" s x.column "^" x.content) xs
|
|
|
|
let molecule s : unit =
|
|
match parse_molecule s with
|
|
| Ok x -> print_molecule x
|
|
| Error xs -> print_error xs s
|
|
|
|
let () = molecule "C6Hi12O6"
|