Skip to content

Commit 96d285f

Browse files
committed
Remove BAD_IDENT
1 parent e4ba71c commit 96d285f

35 files changed

+4458
-247
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
open Rule.Let
2+
3+
let rec match_longest (left_key, left_rule) rules =
4+
match rules with
5+
| [] ->
6+
Rule.Match.bind left_rule (fun value -> return_match (left_key, value))
7+
| new_left :: rest ->
8+
Rule.Match.bind_longest (left_rule, match_longest new_left rest)
9+
(function
10+
| `Left value -> return_match (left_key, value)
11+
| `Right value -> return_match value)
12+
13+
let static rules =
14+
let rec match_everything values = function
15+
| [] -> return_match (List.rev values)
16+
| left :: rest ->
17+
Rule.Match.bind left (fun value ->
18+
match_everything (value :: values) rest)
19+
in
20+
match_everything [] rules
21+
22+
let extract_expected_value error_msg =
23+
if not (String.contains error_msg 'E') then None
24+
else
25+
try
26+
let start = String.index error_msg '\'' + 1 in
27+
let end_idx = String.index_from error_msg start '\'' in
28+
Some (String.sub error_msg start (end_idx - start))
29+
with _ -> None
30+
31+
let extract_got_value error_msg =
32+
if not (String.contains error_msg 'i') then "the provided value"
33+
else
34+
try
35+
let start = String.rindex error_msg '\'' in
36+
let before_quote = String.sub error_msg 0 start in
37+
let second_last = String.rindex before_quote '\'' + 1 in
38+
String.sub error_msg second_last (start - second_last)
39+
with _ -> "the provided value"
40+
41+
let format_expected_values = function
42+
| [] -> ""
43+
| [ single ] -> "'" ^ single ^ "'"
44+
| values ->
45+
let rec format_list = function
46+
| [] -> ""
47+
| [ x ] -> "or '" ^ x ^ "'"
48+
| x :: xs -> "'" ^ x ^ "', " ^ format_list xs
49+
in
50+
format_list values
51+
52+
let create_error_message got expected_values =
53+
match expected_values with
54+
| [] -> [ "Got '" ^ got ^ "'" ]
55+
| values -> (
56+
match Levenshtein.find_closest_match got values with
57+
| Some suggestion ->
58+
[ "Got '" ^ got ^ "', did you mean '" ^ suggestion ^ "'?" ]
59+
| None ->
60+
let expected_str = format_expected_values values in
61+
[ "Got '" ^ got ^ "', expected " ^ expected_str ^ "." ])
62+
63+
let process_error_messages = function
64+
| [] -> [ "No alternatives matched" ]
65+
| errors ->
66+
let expected_values =
67+
errors
68+
|> List.filter_map (function
69+
| msg :: _ -> extract_expected_value msg
70+
| _ -> None)
71+
|> List.filter (fun value -> value <> "$")
72+
|> List.sort_uniq String.compare
73+
in
74+
(match expected_values with
75+
| [] -> List.hd errors
76+
| values ->
77+
let got =
78+
match List.hd errors with
79+
| msg :: _ -> extract_got_value msg
80+
| _ -> "the provided value"
81+
in
82+
create_error_message got values)
83+
84+
let xor rules =
85+
match rules with
86+
| [] -> failwith "xor doesn't make sense without a single value"
87+
| all_rules ->
88+
let try_rules_with_best_match = function
89+
| [] -> failwith "xor doesn't make sense without a single value"
90+
| left :: rest ->
91+
let rules_with_unit = List.map (fun rule -> ((), rule)) rest in
92+
Rule.Match.map
93+
(match_longest ((), left) rules_with_unit)
94+
(fun ((), value) -> value)
95+
in
96+
let try_all_and_collect_errors rules tokens =
97+
let rec collect_errors remaining_rules acc_errors =
98+
match remaining_rules with
99+
| [] ->
100+
let combined_error = process_error_messages acc_errors in
101+
Rule.Data.return (Error combined_error) tokens
102+
| rule :: rest -> (
103+
let data, remaining = rule tokens in
104+
match data with
105+
| Ok value -> Rule.Data.return (Ok value) remaining
106+
| Error err -> collect_errors rest (acc_errors @ [ err ]))
107+
in
108+
collect_errors rules []
109+
in
110+
fun tokens ->
111+
let successful_rules =
112+
all_rules
113+
|> List.filter_map (fun rule ->
114+
let data, remaining = rule tokens in
115+
match data with
116+
| Ok _ -> Some (rule, remaining)
117+
| Error _ -> None)
118+
in
119+
match successful_rules with
120+
| [] -> try_all_and_collect_errors all_rules tokens
121+
| _ -> try_rules_with_best_match all_rules tokens
122+
123+
let and_ rules =
124+
let rec match_everything values indexed_rules =
125+
match indexed_rules with
126+
| [] -> return_match (List.rev values)
127+
| left :: new_rules ->
128+
Rule.Match.bind (match_longest left new_rules) (fun (key, value) ->
129+
let remaining = List.remove_assoc key indexed_rules in
130+
match_everything ((key, value) :: values) remaining)
131+
in
132+
let indexed_rules = List.mapi (fun i rule -> (i, rule)) rules in
133+
Rule.Match.map (match_everything [] indexed_rules) (fun values ->
134+
values
135+
|> List.sort (fun (a, _) (b, _) -> Int.compare a b)
136+
|> List.map (fun (_, v) -> v))
137+
138+
let or_ rules =
139+
rules |> List.map Modifier.optional |> and_ |> Modifier.at_least_one
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Rule = Rule
2+
module Combinators = Combinators
3+
module Modifier = Modifier
4+
module Levenshtein = Levenshtein
5+
module Standard = Standard
6+
module Spec = Spec
7+
module Properties = Properties
8+
module Registry = Registry
9+
module Css_types = Css_types
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Length = struct end
2+
module Angle = struct end
3+
module Time = struct end
4+
module Frequency = struct end
5+
module Resolution = struct end
6+
module Percentage = struct end
7+
module Number = struct end
8+
module Integer = struct end
9+
module FlexValue = struct end
10+
module LengthPercentage = struct end
11+
module Color = struct end
12+
module Cascading = struct end
13+
module Url = struct end
14+
module Margin = struct end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
let distance s1 s2 =
2+
let len1 = String.length s1 in
3+
let len2 = String.length s2 in
4+
let matrix = Array.make_matrix (len1 + 1) (len2 + 1) 0 in
5+
for i = 0 to len1 do
6+
matrix.(i).(0) <- i
7+
done;
8+
for j = 0 to len2 do
9+
matrix.(0).(j) <- j
10+
done;
11+
for i = 1 to len1 do
12+
for j = 1 to len2 do
13+
let cost = if s1.[i - 1] = s2.[j - 1] then 0 else 1 in
14+
matrix.(i).(j) <-
15+
min
16+
(min (matrix.(i - 1).(j) + 1) (matrix.(i).(j - 1) + 1))
17+
(matrix.(i - 1).(j - 1) + cost)
18+
done
19+
done;
20+
matrix.(len1).(len2)
21+
22+
let find_closest_match target candidates =
23+
let target_lower = String.lowercase_ascii target in
24+
let scored_candidates =
25+
candidates
26+
|> List.map (fun candidate ->
27+
let candidate_lower = String.lowercase_ascii candidate in
28+
let dist = distance target_lower candidate_lower in
29+
let max_len = max (String.length target) (String.length candidate) in
30+
let similarity_ratio =
31+
if max_len = 0 then 1.0
32+
else 1.0 -. (float_of_int dist /. float_of_int max_len)
33+
in
34+
(candidate, dist, similarity_ratio))
35+
|> List.sort (fun (_, d1, _) (_, d2, _) -> Int.compare d1 d2)
36+
in
37+
match scored_candidates with
38+
| [] -> None
39+
| (best_match, dist, ratio) :: _ ->
40+
let is_good_match =
41+
dist <= 2 || (ratio >= 0.6 && String.length target > 3)
42+
in
43+
if is_good_match then Some best_match else None
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
open Rule.Let
2+
open Rule.Pattern
3+
module Tokens = Styled_ppx_css_parser.Tokens
4+
5+
type range = int * int option
6+
7+
let one = Fun.id
8+
9+
let optional rule =
10+
Rule.Data.bind rule (fun value ->
11+
let value = match value with Ok value -> Some value | Error _ -> None in
12+
return_match value)
13+
14+
let match_n_values (min, max) sep rule =
15+
let rule_with_sep = Rule.Match.bind sep (fun () -> rule) in
16+
let rec match_until_fails values =
17+
Rule.Data.bind rule_with_sep (fun value ->
18+
let length = List.length values + if Result.is_ok value then 1 else 0 in
19+
let hit_min = length >= min in
20+
let hit_max = match max with Some m -> length >= m | None -> false in
21+
match value with
22+
| Ok value ->
23+
if hit_max then return_match (List.rev (value :: values))
24+
else match_until_fails (value :: values)
25+
| Error last_error ->
26+
if hit_min then return_match (List.rev values)
27+
else return_data (Error last_error))
28+
in
29+
Rule.Data.bind rule (fun value ->
30+
match value with
31+
| Ok value -> match_until_fails [ value ]
32+
| Error last_error ->
33+
if min = 0 then return_data (Ok [])
34+
else return_data (Error last_error))
35+
36+
let zero_or_more rule = match_n_values (0, None) identity rule
37+
let one_or_more rule = match_n_values (1, None) identity rule
38+
let repeat_by_sep sep (min, max) rule = match_n_values (min, max) sep rule
39+
let repeat (min, max) rule = repeat_by_sep identity (min, max) rule
40+
41+
let repeat_by_comma (min, max) rule =
42+
repeat_by_sep (expect Tokens.COMMA) (min, max) rule
43+
44+
let at_least_one rule =
45+
Rule.Match.bind rule (fun values ->
46+
let have_one = List.exists Option.is_some values in
47+
return_data
48+
(if have_one then Ok values else Error [ "should match at least one" ]))
49+
50+
let at_least_one_2 rule =
51+
Rule.Match.bind rule (fun (a, b) ->
52+
Rule.Match.bind
53+
(let a = Option.map (fun a -> `A a) a in
54+
let b = Option.map (fun b -> `B b) b in
55+
at_least_one (return_match [ a; b ]))
56+
(fun _ -> return_match (a, b)))
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(* Box Model *)
2+
module Margin = [%spec_module "[ <length> | <percentage> | 'auto' ]{1,4}"]
3+
module Margin_top = [%spec_module "<length> | <percentage> | 'auto'"]
4+
module Margin_right = [%spec_module "<length> | <percentage> | 'auto'"]
5+
module Margin_bottom = [%spec_module "<length> | <percentage> | 'auto'"]
6+
module Margin_left = [%spec_module "<length> | <percentage> | 'auto'"]
7+
8+
module Padding = [%spec_module "[ <length> | <percentage> ]{1,4}"]
9+
module Padding_top = [%spec_module "<length> | <percentage>"]
10+
module Padding_right = [%spec_module "<length> | <percentage>"]
11+
module Padding_bottom = [%spec_module "<length> | <percentage>"]
12+
module Padding_left = [%spec_module "<length> | <percentage>"]
13+
14+
module Width = [%spec_module "<length> | <percentage> | 'auto'"]
15+
module Height = [%spec_module "<length> | <percentage> | 'auto'"]
16+
module Min_width = [%spec_module "<length> | <percentage> | 'auto'"]
17+
module Min_height = [%spec_module "<length> | <percentage> | 'auto'"]
18+
module Max_width = [%spec_module "<length> | <percentage> | 'none'"]
19+
module Max_height = [%spec_module "<length> | <percentage> | 'none'"]
20+
21+
(* Positioning *)
22+
module Position = [%spec_module "'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'"]
23+
module Top = [%spec_module "<length> | <percentage> | 'auto'"]
24+
module Right = [%spec_module "<length> | <percentage> | 'auto'"]
25+
module Bottom = [%spec_module "<length> | <percentage> | 'auto'"]
26+
module Left = [%spec_module "<length> | <percentage> | 'auto'"]
27+
module Z_index = [%spec_module "'auto' | <integer>"]
28+
29+
(* Display *)
30+
module Display = [%spec_module "'block' | 'inline' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'none'"]
31+
module Visibility = [%spec_module "'visible' | 'hidden' | 'collapse'"]
32+
module Opacity = [%spec_module "<number>"]
33+
module Overflow = [%spec_module "[ 'visible' | 'hidden' | 'scroll' | 'auto' ]{1,2}"]
34+
35+
(* Flexbox *)
36+
module Flex_direction = [%spec_module "'row' | 'row-reverse' | 'column' | 'column-reverse'"]
37+
module Flex_wrap = [%spec_module "'nowrap' | 'wrap' | 'wrap-reverse'"]
38+
module Justify_content = [%spec_module "'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around'"]
39+
module Align_items = [%spec_module "'stretch' | 'flex-start' | 'flex-end' | 'center' | 'baseline'"]
40+
module Align_self = [%spec_module "'auto' | 'stretch' | 'flex-start' | 'flex-end' | 'center' | 'baseline'"]
41+
module Flex_grow = [%spec_module "<number>"]
42+
module Flex_shrink = [%spec_module "<number>"]
43+
module Flex_basis = [%spec_module "<length> | <percentage> | 'auto'"]
44+
module Order = [%spec_module "<integer>"]
45+
module Gap = [%spec_module "[ <length> | <percentage> ]{1,2}"]
46+
47+
(* Typography *)
48+
module Font_size = [%spec_module "<length> | <percentage> | 'small' | 'medium' | 'large'"]
49+
module Font_weight = [%spec_module "<number> | 'normal' | 'bold'"]
50+
module Font_style = [%spec_module "'normal' | 'italic' | 'oblique'"]
51+
module Line_height = [%spec_module "'normal' | <number> | <length> | <percentage>"]
52+
module Letter_spacing = [%spec_module "'normal' | <length>"]
53+
module Text_align = [%spec_module "'left' | 'right' | 'center' | 'justify'"]
54+
module Text_decoration = [%spec_module "'none' | 'underline' | 'overline' | 'line-through'"]
55+
module Text_transform = [%spec_module "'none' | 'capitalize' | 'uppercase' | 'lowercase'"]
56+
module White_space = [%spec_module "'normal' | 'nowrap' | 'pre' | 'pre-wrap' | 'pre-line'"]
57+
58+
(* Colors *)
59+
module Color = [%spec_module "<hex-color> | 'currentColor' | 'transparent'"]
60+
module Background_color = Color
61+
62+
(* Borders *)
63+
module Border_width = [%spec_module "[ <length> | 'thin' | 'medium' | 'thick' ]{1,4}"]
64+
module Border_style = [%spec_module "[ 'none' | 'solid' | 'dashed' | 'dotted' ]{1,4}"]
65+
module Border_color = [%spec_module "[ <hex-color> | 'currentColor' | 'transparent' ]{1,4}"]
66+
module Border_radius = [%spec_module "[ <length> | <percentage> ]{1,4}"]
67+
68+
(* Background *)
69+
module Background_image = [%spec_module "'none' | <url>"]
70+
module Background_size = [%spec_module "[ <length> | <percentage> | 'auto' | 'cover' | 'contain' ]+"]
71+
module Background_repeat = [%spec_module "'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat'"]
72+
73+
(* Cursor *)
74+
module Cursor = [%spec_module "'auto' | 'default' | 'pointer' | 'text' | 'wait' | 'move'"]
75+
module Pointer_events = [%spec_module "'auto' | 'none'"]
76+
module User_select = [%spec_module "'auto' | 'none' | 'text' | 'all'"]
77+
78+
(* Box Sizing *)
79+
module Box_sizing = [%spec_module "'content-box' | 'border-box'"]
80+
module Object_fit = [%spec_module "'fill' | 'contain' | 'cover' | 'none' | 'scale-down'"]
81+
82+
(* Float and Clear *)
83+
module Float = [%spec_module "'left' | 'right' | 'none'"]
84+
module Clear = [%spec_module "'none' | 'left' | 'right' | 'both'"]
85+
86+
(* Table *)
87+
module Table_layout = [%spec_module "'auto' | 'fixed'"]
88+
module Border_collapse = [%spec_module "'collapse' | 'separate'"]
89+
90+
(* Transitions *)
91+
module Transition_duration = [%spec_module "<time>"]
92+
module Transition_delay = [%spec_module "<time>"]
93+
94+
(* Others *)
95+
module Content = [%spec_module "'none' | 'normal' | <string>"]
96+
module Resize = [%spec_module "'none' | 'both' | 'horizontal' | 'vertical'"]

0 commit comments

Comments
 (0)