161 lines
6.8 KiB
OCaml
161 lines
6.8 KiB
OCaml
let rec split_at1 l n =
|
|
if n = 0 then ([], l) else
|
|
match l with
|
|
| [] -> ([], [])
|
|
| h :: t -> let (l1, l2) = split_at1 t (n-1) in (h :: l1, l2);;
|
|
|
|
let safe_hd xs =
|
|
match xs with
|
|
| [] -> None
|
|
| x :: _ -> Some x
|
|
|
|
let safe_tl xs =
|
|
match xs with
|
|
| [] -> None
|
|
| _ :: xs -> Some xs
|
|
|
|
|
|
let safe_list xs =
|
|
match List.length xs with
|
|
| 0 -> (None, None)
|
|
| 1 -> ((Some (List.hd xs)), None)
|
|
| _ -> ((Some (List.hd xs)), Some(List.tl xs))
|
|
|
|
let list_skip xs n =
|
|
match split_at1 xs n with
|
|
| (_, tl) -> tl
|
|
|
|
let if_list_peek xs n v =
|
|
match List.nth_opt xs n with
|
|
| Some x -> x = v
|
|
| _ -> false
|
|
|
|
module Parser = struct
|
|
include Token
|
|
|
|
let split_with (l:Token.token_type list) left right =
|
|
let f (length, level) (i, tk) =
|
|
if level < 1 && i < length then
|
|
(i, level)
|
|
else
|
|
match tk with
|
|
| a when Token.equal a left -> (length, level + 1)
|
|
| a when Token.equal a right -> (length, level - 1)
|
|
| _ -> (length, level)
|
|
in
|
|
match List.fold_left f (List.length l, 1) (List.mapi (fun i e -> (i,e)) l) with
|
|
| (i, _) -> Some (split_at1 l i)
|
|
|
|
module Obj = Map.Make(String);;
|
|
|
|
type hvalue =
|
|
| INTEGER of int
|
|
| FLOAT of float
|
|
| STRING of string
|
|
| BINARY of Bytes.t
|
|
| BOOL of bool
|
|
| NULL
|
|
| ARRAY of hvalue List.t
|
|
| OBJECT of hvalue Obj.t
|
|
|
|
let rec equal a b =
|
|
match (a,b) with
|
|
| (INTEGER a, INTEGER b) -> Int.equal a b
|
|
| (FLOAT a, FLOAT b) -> Float.equal a b
|
|
| (STRING a, STRING b) -> String.equal a b
|
|
| (BINARY a, BINARY b) -> Bytes.equal a b
|
|
| (BOOL a, BOOL b) -> Bool.equal a b
|
|
| (NULL, NULL) -> true
|
|
| (ARRAY a, ARRAY b) -> List.equal equal a b
|
|
| (OBJECT a, OBJECT b) -> Obj.equal equal a b
|
|
| _ -> false
|
|
|
|
let stringify_bytes b = Bytes.fold_left (fun acc b -> acc ^ Printf.sprintf "\\x%2x" (Char.code b)) "" b
|
|
|
|
let rec hvalue_to_string = function
|
|
| INTEGER a -> "INTEGER: " ^ Int.to_string a
|
|
| FLOAT a -> "INTEGER: " ^ Float.to_string a
|
|
| STRING a -> "INTEGER: " ^ a
|
|
| BINARY a -> "BINARY " ^ Bytes.fold_left (fun acc b -> acc ^ Printf.sprintf "\\x%2x" (Char.code b)) "" a
|
|
| BOOL a -> "BOOL: " ^ Bool.to_string a
|
|
| NULL -> "NULL"
|
|
| ARRAY a -> "ARRAY [" ^ (String.concat ", " (List.map hvalue_to_string a)) ^ "]"
|
|
| OBJECT a -> "OBJECT {" ^ (String.concat ", " (List.map (fun (k,v) -> k ^ ": (" ^ (hvalue_to_string v) ^ ")") (Obj.to_list a) )) ^ "}"
|
|
|
|
let parse_primitive token =
|
|
match token with
|
|
| Token.INTEGER a -> INTEGER a
|
|
| Token.FLOAT a -> FLOAT a
|
|
| Token.STRING a -> STRING a
|
|
| Token.BINARY a -> BINARY a
|
|
| Token.BOOL a -> BOOL a
|
|
| Token.NULL -> NULL
|
|
| _ -> failwith "Not a primitive token"
|
|
|
|
let print_token_array tokens =
|
|
List.map Token.token_to_string tokens |> String.concat "; "
|
|
|
|
|
|
let rec parse tokens =
|
|
match safe_hd tokens with
|
|
| Some(Token.INTEGER _ | Token.FLOAT _ | Token.STRING _ | Token.BINARY _ | Token.BOOL _ | Token.NULL as t) -> parse_primitive t
|
|
| Some(Token.LBRACE) -> (match safe_tl tokens with
|
|
| Some(tl) -> parse_object tl
|
|
| _ -> failwith "Not a valid hcfg file")
|
|
|
|
| Some(Token.LBRACKET) -> (match safe_tl tokens with
|
|
| Some(tl) -> parse_arr tl
|
|
| _ -> failwith "Not a valid hcfg file")
|
|
| Some(Token.EOF) -> failwith "Empty tree"
|
|
| _ -> failwith "Not a valid hcfg file"
|
|
and parse_object tokens =
|
|
let rec parse_object_i tkns acc =
|
|
match safe_list tkns with
|
|
| (Some hd, Some tl) -> (match hd with
|
|
| Token.IDENT i when if_list_peek tl 0 Token.COLON -> (match List.nth_opt tl 1 with
|
|
| Some(Token.INTEGER _ | Token.FLOAT _ | Token.STRING _ | Token.BINARY _ | Token.BOOL _ | Token.NULL as t) when if_list_peek tl 2 Token.COMMA -> let (_arr, rest) = split_at1 tl 3 in parse_object_i rest (Obj.add i (parse_primitive t) acc)
|
|
| Some(Token.INTEGER _ | Token.FLOAT _ | Token.STRING _ | Token.BINARY _ | Token.BOOL _ | Token.NULL as t) when if_list_peek tl 2 Token.RBRACE -> let (_arr, rest) = split_at1 tl 2 in parse_object_i rest (Obj.add i (parse_primitive t) acc)
|
|
| Some Token.LBRACKET -> (match split_with (list_skip tl 2) Token.LBRACKET Token.RBRACKET with
|
|
| Some (arr, rest) ->
|
|
parse_object_i (rest) (Obj.add i (parse_arr arr) acc)
|
|
| None -> failwith "uhhh arr in obj")
|
|
| Some Token.LBRACE -> (match split_with (list_skip tl 2) Token.LBRACE Token.RBRACE with
|
|
| Some (arr, rest) -> parse_object_i (rest) (Obj.add i (parse_object arr) acc)
|
|
| None -> failwith "uhhh arr in obj")
|
|
| _ -> "TODO: Unhandled token for object value" ^ Token.token_to_string hd ^ "(" ^ print_token_array tl ^ ")" |> failwith
|
|
)
|
|
| Token.COMMA when not (if_list_peek tl 0 Token.RBRACE) -> parse_object_i tl acc
|
|
| Token.COMMA when if_list_peek tl 0 Token.RBRACE -> acc
|
|
| Token.RBRACE -> acc
|
|
| _ -> "TODO: Unhandled token in object: " ^ (Token.token_to_string hd) ^ " (" ^ (print_token_array tl) ^ ")"|> failwith)
|
|
| (Some Token.RBRACE, _) -> acc
|
|
| _ -> (String.concat ", " (List.map (fun (k,v) -> k ^ ": (" ^ (hvalue_to_string v) ^ ")") (Obj.to_list acc))) |> failwith
|
|
in OBJECT (parse_object_i tokens (Obj.empty))
|
|
and parse_arr tokens =
|
|
let rec parse_arr_i tkns acc =
|
|
match safe_list tkns with
|
|
| (Some hd, Some tl) -> (match hd with
|
|
| Token.INTEGER _ | Token.FLOAT _ | Token.STRING _ | Token.BINARY _ | Token.NULL as t -> (match safe_list tl with
|
|
| (Some Token.COMMA, Some tl) -> (match safe_hd tl with
|
|
| Some Token.EOF -> failwith "Unterminated Array"
|
|
| Some Token.RBRACKET -> ((parse_primitive t) :: acc)
|
|
| _ -> parse_arr_i tl ((parse_primitive t) :: acc)
|
|
)
|
|
| (Some Token.RBRACKET, _) -> ((parse_primitive t) :: acc)
|
|
| (Some Token.EOF, _) -> failwith "Unterminated Array"
|
|
| _ -> "Invalid Array (" ^ print_token_array tl ^ ")" |> failwith
|
|
)
|
|
| Token.LBRACKET -> (match split_with tl Token.LBRACKET Token.RBRACKET with
|
|
| Some (arr, rest) -> parse_arr_i (rest) (parse_arr arr :: acc)
|
|
| None -> failwith "uhhh arr in arr")
|
|
| Token.LBRACE -> (match split_with tl Token.LBRACE Token.RBRACE with
|
|
| Some (arr, rest) -> parse_arr_i (rest) (parse_object arr :: acc)
|
|
| None -> failwith "uhhh obj in arr")
|
|
| Token.RBRACKET -> acc
|
|
| t -> "TODO: Unhandled token in array: " ^ (Token.token_to_string t) ^ " (" ^ (print_token_array tl) ^ ")"|> failwith
|
|
)
|
|
| (Some Token.EOF, None) -> acc
|
|
| (Some Token.RBRACKET, None) -> acc
|
|
| _ -> "TODO: parse_arr (" ^ (print_token_array tkns) ^ ")"|> failwith
|
|
in ARRAY (List.rev (parse_arr_i tokens []))
|
|
end |