summaryrefslogtreecommitdiff
path: root/coqpp/coqpp_lex.mll
blob: bfa4e2b57b06f077cd393b790d98ea4c4a2b820b (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
(************************************************************************)
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team     *)
(* <O___,, *   INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2017     *)
(*   \VV/  **************************************************************)
(*    //   *      This file is distributed under the terms of the       *)
(*         *       GNU Lesser General Public License Version 2.1        *)
(************************************************************************)

{

open Lexing
open Coqpp_parse

type mode =
| OCaml
| Extend

exception Lex_error of Coqpp_ast.loc * string

let loc lexbuf = {
  Coqpp_ast.loc_start = lexeme_start_p lexbuf;
  Coqpp_ast.loc_end = lexeme_end_p lexbuf;
}

let newline lexbuf =
  let pos = lexbuf.lex_curr_p in
  let pos = { pos with pos_lnum = pos.pos_lnum + 1; pos_bol = pos.pos_cnum } in
  lexbuf.lex_curr_p <- pos

let num_comments = ref 0
let num_braces = ref 0

let mode () = if !num_braces = 0 then Extend else OCaml

let comment_buf = Buffer.create 512
let ocaml_buf = Buffer.create 512
let string_buf = Buffer.create 512

let lex_error lexbuf msg =
  raise (Lex_error (loc lexbuf, msg))

let lex_unexpected_eof lexbuf where =
  lex_error lexbuf (Printf.sprintf "Unexpected end of file in %s" where)

let start_comment _ =
  let () = incr num_comments in
  Buffer.add_string comment_buf "(*"

let end_comment lexbuf =
  let () = decr num_comments in
  let () = Buffer.add_string comment_buf "*)" in
  if !num_comments < 0 then lex_error lexbuf "Unexpected end of comment"
  else if !num_comments = 0 then
    let s = Buffer.contents comment_buf in
    let () = Buffer.reset comment_buf in
    Some (COMMENT s)
  else
    None

let start_ocaml _ =
  let () = match mode () with
  | OCaml -> Buffer.add_string ocaml_buf "{"
  | Extend -> ()
  in
  incr num_braces

let end_ocaml lexbuf =
  let () = decr num_braces in
  if !num_braces < 0 then lex_error lexbuf "Unexpected end of OCaml code"
  else if !num_braces = 0 then
    let s = Buffer.contents ocaml_buf in
    let () = Buffer.reset ocaml_buf in
    Some (CODE { Coqpp_ast.code = s })
  else
    let () = Buffer.add_string ocaml_buf "}" in
    None

}

let letter = ['a'-'z' 'A'-'Z']
let letterlike = ['_' 'a'-'z' 'A'-'Z']
let alphanum = ['_' 'a'-'z' 'A'-'Z' '0'-'9' '\'']
let ident = letterlike alphanum*
let qualid = ident ('.' ident)*
let space = [' ' '\t' '\r']
let number = [ '0'-'9' ]

rule extend = parse
| "(*" { start_comment (); comment lexbuf }
| "{" { start_ocaml (); ocaml lexbuf }
| "GRAMMAR" { GRAMMAR }
| "VERNAC" { VERNAC }
| "TACTIC" { TACTIC }
| "EXTEND" { EXTEND }
| "END" { END }
| "DECLARE" { DECLARE }
| "PLUGIN" { PLUGIN }
| "DEPRECATED" { DEPRECATED }
(** Camlp5 specific keywords *)
| "GLOBAL" { GLOBAL }
| "FIRST" { FIRST }
| "LAST" { LAST }
| "BEFORE" { BEFORE }
| "AFTER" { AFTER }
| "LEVEL" { LEVEL }
| "LEFTA" { LEFTA }
| "RIGHTA" { RIGHTA }
| "NONA" { NONA }
(** Standard *)
| ident { IDENT (Lexing.lexeme lexbuf) }
| qualid { QUALID (Lexing.lexeme lexbuf) }
| number { INT (int_of_string (Lexing.lexeme lexbuf)) }
| space { extend lexbuf }
| '\"' { string lexbuf }
| '\n' { newline lexbuf; extend lexbuf }
| '[' { LBRACKET }
| ']' { RBRACKET }
| '|' { PIPE }
| "->" { ARROW }
| ',' { COMMA }
| ':' { COLON }
| ';' { SEMICOLON }
| '(' { LPAREN }
| ')' { RPAREN }
| '=' { EQUAL }
| _ { lex_error lexbuf "syntax error" }
| eof { EOF }

and ocaml = parse
| "{" { start_ocaml (); ocaml lexbuf }
| "}" { match end_ocaml lexbuf with Some tk -> tk | None -> ocaml lexbuf }
| '\n' { newline lexbuf; Buffer.add_char ocaml_buf '\n'; ocaml lexbuf }
| '\"' { Buffer.add_char ocaml_buf '\"'; ocaml_string lexbuf }
| (_ as c) { Buffer.add_char ocaml_buf c; ocaml lexbuf }
| eof { lex_unexpected_eof lexbuf "OCaml code" }

and comment = parse
| "*)" { match end_comment lexbuf with Some _ -> extend lexbuf | None -> comment lexbuf }
| "(*" { start_comment lexbuf; comment lexbuf }
| '\n' { newline lexbuf; Buffer.add_char comment_buf '\n'; comment lexbuf }
| (_ as c) { Buffer.add_char comment_buf c; comment lexbuf }
| eof { lex_unexpected_eof lexbuf "comment" }

and string = parse
| '\"'
  {
    let s = Buffer.contents string_buf in
    let () = Buffer.reset string_buf in
    STRING s
  }
| "\\\"" { Buffer.add_char string_buf '\"'; string lexbuf }
| '\n' { newline lexbuf; Buffer.add_char string_buf '\n'; string lexbuf }
| (_ as c) { Buffer.add_char string_buf c; string lexbuf }
| eof { lex_unexpected_eof lexbuf "string" }

and ocaml_string = parse
| "\\\"" { Buffer.add_string ocaml_buf "\\\""; ocaml_string lexbuf }
| '\"' { Buffer.add_char ocaml_buf '\"'; ocaml lexbuf }
| (_ as c) { Buffer.add_char ocaml_buf c; ocaml_string lexbuf }
| eof { lex_unexpected_eof lexbuf "OCaml string" }

{

let token lexbuf = match mode () with
| OCaml -> ocaml lexbuf
| Extend -> extend lexbuf

}