aboutsummaryrefslogtreecommitdiff
path: root/gramlib/ploc.ml
blob: cb71f726787eaaabeafbad59fd697f52176095d5 (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
(* camlp5r *)
(* ploc.ml,v *)
(* Copyright (c) INRIA 2007-2017 *)

type t =
  { fname : string;
    line_nb : int;
    bol_pos : int;
    line_nb_last : int;
    bol_pos_last : int;
    bp : int;
    ep : int;
    comm : string;
    ecomm : string }

let make_loc fname line_nb bol_pos (bp, ep) comm =
  {fname = fname; line_nb = line_nb; bol_pos = bol_pos;
   line_nb_last = line_nb; bol_pos_last = bol_pos; bp = bp; ep = ep;
   comm = comm; ecomm = ""}

let make_unlined (bp, ep) =
  {fname = ""; line_nb = 1; bol_pos = 0; line_nb_last = -1; bol_pos_last = 0;
   bp = bp; ep = ep; comm = ""; ecomm = ""}

let dummy =
  {fname = ""; line_nb = 1; bol_pos = 0; line_nb_last = -1; bol_pos_last = 0;
   bp = 0; ep = 0; comm = ""; ecomm = ""}

let file_name loc = loc.fname
let first_pos loc = loc.bp
let last_pos loc = loc.ep
let line_nb loc = loc.line_nb
let bol_pos loc = loc.bol_pos
let line_nb_last loc = loc.line_nb_last
let bol_pos_last loc = loc.bol_pos_last
let comment loc = loc.comm
let comment_last loc = loc.ecomm

(* *)

let encl loc1 loc2 =
  if loc1.bp < loc2.bp then
    if loc1.ep < loc2.ep then
      {fname = loc1.fname; line_nb = loc1.line_nb; bol_pos = loc1.bol_pos;
       line_nb_last = loc2.line_nb_last; bol_pos_last = loc2.bol_pos_last;
       bp = loc1.bp; ep = loc2.ep; comm = loc1.comm; ecomm = loc2.comm}
    else loc1
  else if loc2.ep < loc1.ep then
    {fname = loc2.fname; line_nb = loc2.line_nb; bol_pos = loc2.bol_pos;
     line_nb_last = loc1.line_nb_last; bol_pos_last = loc1.bol_pos_last;
     bp = loc2.bp; ep = loc1.ep; comm = loc2.comm; ecomm = loc1.comm}
  else loc2
let shift sh loc = {loc with bp = sh + loc.bp; ep = sh + loc.ep}
let sub loc sh len = {loc with bp = loc.bp + sh; ep = loc.bp + sh + len}
let after loc sh len = {loc with bp = loc.ep + sh; ep = loc.ep + sh + len}
let with_comment loc comm = {loc with comm = comm}

let name = ref "loc"

let from_file fname loc =
  let (bp, ep) = first_pos loc, last_pos loc in
  try
    let ic = open_in_bin fname in
    let strm = Stream.of_channel ic in
    let rec loop fname lin =
      let rec not_a_line_dir col (strm__ : _ Stream.t) =
        let cnt = Stream.count strm__ in
        match Stream.peek strm__ with
          Some c ->
            Stream.junk strm__;
            let s = strm__ in
            if cnt < bp then
              if c = '\n' then loop fname (lin + 1)
              else not_a_line_dir (col + 1) s
            else let col = col - (cnt - bp) in fname, lin, col, col + ep - bp
        | _ -> fname, lin, col, col + 1
      in
      let rec a_line_dir str n col (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some '\n' -> Stream.junk strm__; loop str n
        | Some _ -> Stream.junk strm__; a_line_dir str n (col + 1) strm__
        | _ -> raise Stream.Failure
      in
      let rec spaces col (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some ' ' -> Stream.junk strm__; spaces (col + 1) strm__
        | _ -> col
      in
      let rec check_string str n col (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some '"' ->
            Stream.junk strm__;
            let col =
              try spaces (col + 1) strm__ with
                Stream.Failure -> raise (Stream.Error "")
            in
            a_line_dir str n col strm__
        | Some c when c <> '\n' ->
            Stream.junk strm__;
            check_string (str ^ String.make 1 c) n (col + 1) strm__
        | _ -> not_a_line_dir col strm__
      in
      let check_quote n col (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some '"' -> Stream.junk strm__; check_string "" n (col + 1) strm__
        | _ -> not_a_line_dir col strm__
      in
      let rec check_num n col (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some ('0'..'9' as c) ->
            Stream.junk strm__;
            check_num (10 * n + Char.code c - Char.code '0') (col + 1) strm__
        | _ -> let col = spaces col strm__ in check_quote n col strm__
      in
      let begin_line (strm__ : _ Stream.t) =
        match Stream.peek strm__ with
          Some '#' ->
            Stream.junk strm__;
            let col =
              try spaces 1 strm__ with
                Stream.Failure -> raise (Stream.Error "")
            in
            check_num 0 col strm__
        | _ -> not_a_line_dir 0 strm__
      in
      begin_line strm
    in
    let r =
      try loop fname 1 with
        Stream.Failure ->
          let bol = bol_pos loc in fname, line_nb loc, bp - bol, ep - bol
    in
    close_in ic; r
  with Sys_error _ -> fname, 1, bp, ep

let second_line fname ep0 (line, bp) ep =
  let ic = open_in fname in
  seek_in ic bp;
  let rec loop line bol p =
    if p = ep then
      begin close_in ic; if bol = bp then line, ep0 else line, ep - bol end
    else
      let (line, bol) =
        match input_char ic with
          '\n' -> line + 1, p + 1
        | _ -> line, bol
      in
      loop line bol (p + 1)
  in
  loop line bp bp

let get loc =
  if loc.fname = "" || loc.fname = "-" then
    loc.line_nb, loc.bp - loc.bol_pos, loc.line_nb, loc.ep - loc.bol_pos,
    loc.ep - loc.bp
  else
    let (bl, bc, ec) =
      loc.line_nb, loc.bp - loc.bol_pos, loc.ep - loc.bol_pos
    in
    let (el, eep) = second_line loc.fname ec (bl, loc.bp) loc.ep in
    bl, bc, el, eep, ec - bc

let call_with r v f a =
  let saved = !r in
  try r := v; let b = f a in r := saved; b with e -> r := saved; raise e

exception Exc of t * exn

let raise loc exc =
  match exc with
    Exc (_, _) -> raise exc
  | _ -> raise (Exc (loc, exc))

type 'a vala =
    VaAnt of string
  | VaVal of 'a