package cascade

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file syntax.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
let is_ascii_ident_start = function
  | 'a' .. 'z' | 'A' .. 'Z' | '_' | '-' -> true
  | _ -> false

let is_ascii_ident_continue = function
  | '0' .. '9' -> true
  | c -> is_ascii_ident_start c

let is_hex = function
  | '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' -> true
  | _ -> false

(* Sec. 4.3.9 opens no ident on [-] then a digit, and a bare [-] is a delim, so
   both shapes slip past a per-character scan that reads [-] as ident-start. *)
let starts_dash_digit s =
  String.length s >= 2 && s.[0] = '-' && s.[1] >= '0' && s.[1] <= '9'

let rec ascii_ident_continue_from s n i =
  i >= n
  || (is_ascii_ident_continue s.[i] && ascii_ident_continue_from s n (i + 1))

(* Sec. 4.2 shares one range list between ident-start and ident code points, so
   [-] and the digits are the only positional difference. *)
let is_ident_start_cp cp =
  if cp < 0x80 then is_ascii_ident_start (Char.unsafe_chr cp)
  else Lexer.spec_non_ascii_ident_cp cp

let is_ident_cp cp =
  if cp < 0x80 then is_ascii_ident_continue (Char.unsafe_chr cp)
  else Lexer.spec_non_ascii_ident_cp cp

(* [i] is a byte offset, so [i = 0] is the first code point. Malformed UTF-8
   names no code point and so names no ident. *)
let ident_code_point ok i = function
  | Common.String.Scalar u ->
      let cp = Uchar.to_int u in
      ok && if i = 0 then is_ident_start_cp cp else is_ident_cp cp
  | Common.String.Malformed _ -> false

let is_ident s =
  let n = String.length s in
  if n = 0 || starts_dash_digit s || (n = 1 && s.[0] = '-') then false
  else if is_ascii_ident_start s.[0] && ascii_ident_continue_from s n 1 then
    true
  else Common.String.utf8_fold ident_code_point true s

let url_needs_quotes =
  String.exists (function
    | ' ' | ')' | '"' | '\'' | '(' | '\\' -> true
    | _ -> false)

let split_top_level_colon cvs =
  let rec loop before = function
    | [] -> None
    | Component.Preserved { kind = Token.Colon; _ } :: after ->
        Some (List.rev before, after)
    | cv :: rest -> loop (cv :: before) rest
  in
  loop [] cvs

let strip_url_suffix url =
  Uri.of_string url |> Uri.with_uri ~query:None ~fragment:None |> Uri.to_string

let url_file_path url = Uri.of_string url |> Uri.path |> Uri.pct_decode

let is_remote_url url =
  let uri = Uri.of_string url in
  Option.is_some (Uri.scheme uri) || Option.is_some (Uri.host uri)