package liquid_parser

  1. Overview
  2. Docs

Source file bounds.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
open Base
open Liquid_syntax
open Syntax
open Tools

type bound_finders = {
    start : lex_token
  ; stop : lex_token
  ; other : lex_token list option
  ; conflicts : bound_finders list option
}

let p_to_b start stop = { start; stop; other = None; conflicts = None }
let pair_to_bounds start stop = Some (p_to_b start stop)

(* Several tags rely on the 'else' keyword. This list allows these tags to be tallied when needed *)
let else_conflicts =
  Some
    [
      p_to_b If EndIf
    ; p_to_b Case EndCase
    ; p_to_b Unless EndUnless
    ; p_to_b LexFor LexEndFor
    ]

let bounds_from_opener = function
  | If ->
      Some
        {
          start = If
        ; stop = EndIf
        ; other = Some [ Else; ElseIf ]
        ; conflicts = else_conflicts
        }
  | Case ->
      Some
        {
          start = Case
        ; stop = EndCase
        ; other = Some [ When; Else ]
        ; conflicts = else_conflicts
        }
  | Unless ->
      Some
        {
          start = Unless
        ; stop = EndUnless
        ; other = Some [ Else ]
        ; conflicts = else_conflicts
        }
  | LexFor ->
      Some
        {
          start = LexFor
        ; stop = LexEndFor
        ; other = Some [ Else ]
        ; conflicts = else_conflicts
        }
  | Raw -> pair_to_bounds Raw EndRaw
  | LexStyle -> pair_to_bounds LexStyle LexEndStyle
  | LexForm -> pair_to_bounds LexForm LexEndForm
  | Capture -> pair_to_bounds Capture EndCapture
  | Paginate -> pair_to_bounds Paginate EndPaginate
  | TableRow -> pair_to_bounds TableRow EndTableRow
  | _ -> None

let remove_last lst =
  match List.rev lst with _ :: hds -> List.rev hds | [] -> []

let pair_up_bounds bounds =
  let rec aux acc = function
    | ft :: sd :: td :: tl -> aux (acc @ [ (ft, sd); (sd, td) ]) (td :: tl)
    | ft :: sd :: tl -> aux (acc @ [ (ft, sd) ]) tl
    | [] | [ _ ] -> acc
  in
  aux [] bounds

type conflict_type = Start | End

let has_conflict conflict_type bounds token =
  match bounds.conflicts with
  | Some conflicts ->
      let extract_tag b =
        match conflict_type with Start -> b.start | End -> b.stop
      in
      let starts = List.map conflicts ~f:extract_tag in
      contains starts token
  | _ -> false

let has_start_conflict = has_conflict Start
let has_end_conflict = has_conflict End

let find_bounds tokens start_point =
  let first_token = nth tokens start_point in
  let bounds =
    match bounds_from_opener first_token with
    | Some b -> b
    | None -> raise (Failure "This is not an opening tag")
  in

  let other_bounds = unwrap_or bounds.other [] in

  let folder (tally, found) index =
    let token = nth tokens index in
    match token with
    | _ when token = bounds.start || has_start_conflict bounds token ->
        let acc = (tally @ [ index ], found) in
        Next (acc, index + 1)
    | _ when token = bounds.stop || has_end_conflict bounds token -> (
        let nt = remove_last tally in
        let nf =
          if List.hd_exn found = List.last_exn tally then found @ [ index ]
          else found
        in
        let acc = (nt, nf) in
        match nt with [] -> Stop acc | _ -> Next (acc, index + 1))
    | _ when contains other_bounds token ->
        let nf =
          if first found = List.last_exn tally then found @ [ index ] else found
        in
        let acc = (tally, nf) in
        Next (acc, index + 1)
    | _ ->
        let acc = (tally, found) in
        Next (acc, index + 1)
  in

  let _, found =
    unfold ([ start_point ], [ start_point ]) (start_point + 1) folder
  in
  found |> pair_up_bounds

let bounds_to_chunks tokens bounds =
  List.map bounds ~f:(fun (start_i, end_i) -> sub_list tokens start_i end_i)

let stop_point_from_bounds bounds =
  let _, sp = List.last_exn bounds in
  sp + 1