(*********************************************************************************)
(*                OCaml-RDF                                                      *)
(*                                                                               *)
(*    Copyright (C) 2012-2024 Institut National de Recherche en Informatique     *)
(*    et en Automatique. All rights reserved.                                    *)
(*                                                                               *)
(*    This program is free software; you can redistribute it and/or modify       *)
(*    it under the terms of the GNU Lesser General Public License version        *)
(*    3 as published by the Free Software Foundation.                            *)
(*                                                                               *)
(*    This program is distributed in the hope that it will be useful,            *)
(*    but WITHOUT ANY WARRANTY; without even the implied warranty of             *)
(*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *)
(*    GNU General Public License for more details.                               *)
(*                                                                               *)
(*    You should have received a copy of the GNU General Public License          *)
(*    along with this program; if not, write to the Free Software                *)
(*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                   *)
(*    02111-1307  USA                                                            *)
(*                                                                               *)
(*    Contact: Maxence.Guesdon@inria.fr                                          *)
(*                                                                               *)
(*********************************************************************************)

(** *)

(* https://www.rfc-editor.org/rfc/rfc5646.html#section-2.1 *)

module L = Sedlexing.Utf8

let digit = [%sedlex.regexp? '0'..'9']
let alpha = [%sedlex.regexp? 'a'..'z' | 'A'..'Z']
let alphanum = [%sedlex.regexp? digit | alpha]

let script = [%sedlex.regexp? alpha,alpha,alpha,alpha]
let region = [%sedlex.regexp? (alpha,alpha) | (digit,digit,digit)]

let singleton = [%sedlex.regexp? digit
  | 0x41 .. 0x57 | 0x59 .. 0x5A | 0x61 .. 0x77 | 0x79 .. 0x7A ]

let privateuse = [%sedlex.regexp? "x", Plus("-",alphanum,alphanum,Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum))]

let extension = [%sedlex.regexp?
    singleton, Plus("-", alphanum,alphanum,Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum),Opt(alphanum))]

let variant = [%sedlex.regexp?
    (alphanum,alphanum,alphanum,alphanum,alphanum,Opt(alphanum),Opt(alphanum),Opt(alphanum))
  | (digit, alphanum, alphanum, alphanum)
  ]

let extlang = [%sedlex.regexp?
    (alpha,alpha,alpha)
  | (("-",alpha,alpha,alpha),Plus("-",alpha,alpha,alpha))
  ]

let language = [%sedlex.regexp?
    (alpha,alpha,Opt(alpha),Opt("-",extlang))
  | (alpha,alpha,alpha,alpha),Opt(alpha),Opt(alpha),Opt(alpha),Opt(alpha)]

let langtag = [%sedlex.regexp? language,
    Opt("-", script),
    Opt('-', region),
    Star("-", variant),
    Star("-", extension),
    Opt("-", privateuse) ]

let irregular = [%sedlex.regexp?
    "en-GB-oed" | "i-ami" | "i-bnn" | "i-default"
  | "i-enochian" | "i-hak" | "i-klingon" | "i-lux"
  | "i-mingo" | "i-navajo" | "i-pwn" | "i-tao"
  | "i-tay" | "i-tsu" | "sgn-BE-FR" | "sgn-BE-NL"
  | "sgn-CH-DE" ]

let regular = [%sedlex.regexp?
    "art-lojban"
  | "cel-gaulish"
  | "no-bok"
  | "no-nyn"
  | "zh-guoyu"
  | "zh-hakka"
  | "zh-min"
  | "zh-min-nan"
  | "zh-xiang"
  ]

let grandfathered = [%sedlex.regexp? irregular | regular ]

let language_tag = [%sedlex.regexp? (langtag | privateuse | grandfathered), eof ]

let language_tag lexbuf =
  match%sedlex lexbuf with
  | language_tag -> ()
  | _ -> failwith "Invalid language tag"

let is_valid_language_tag str =
  let lexbuf = Sedlexing.Utf8.from_string str in
  try language_tag lexbuf; true
  with Failure _ -> false
