berghofe@41809
|
1 |
(* Title: HOL/SPARK/Tools/fdl_lexer.ML
|
berghofe@41809
|
2 |
Author: Stefan Berghofer
|
berghofe@41809
|
3 |
Copyright: secunet Security Networks AG
|
berghofe@41809
|
4 |
|
berghofe@41809
|
5 |
Lexical analyzer for fdl files.
|
berghofe@41809
|
6 |
*)
|
berghofe@41809
|
7 |
|
berghofe@41809
|
8 |
signature FDL_LEXER =
|
berghofe@41809
|
9 |
sig
|
berghofe@41809
|
10 |
type T
|
berghofe@41809
|
11 |
type chars
|
berghofe@41809
|
12 |
type banner
|
berghofe@41809
|
13 |
type date
|
berghofe@41809
|
14 |
type time
|
berghofe@41809
|
15 |
datatype kind = Keyword | Ident | Long_Ident | Traceability | Number | Comment | EOF
|
berghofe@41809
|
16 |
val tokenize: (chars -> 'a * chars) -> (chars -> T * chars) ->
|
berghofe@41809
|
17 |
Position.T -> string -> 'a * T list
|
berghofe@41809
|
18 |
val position_of: T -> Position.T
|
berghofe@41809
|
19 |
val pos_of: T -> string
|
berghofe@41809
|
20 |
val is_eof: T -> bool
|
berghofe@41809
|
21 |
val stopper: T Scan.stopper
|
berghofe@41809
|
22 |
val kind_of: T -> kind
|
berghofe@41809
|
23 |
val content_of: T -> string
|
berghofe@41809
|
24 |
val unparse: T -> string
|
berghofe@41809
|
25 |
val is_proper: T -> bool
|
berghofe@41809
|
26 |
val is_digit: string -> bool
|
berghofe@41809
|
27 |
val c_comment: chars -> T * chars
|
berghofe@41809
|
28 |
val curly_comment: chars -> T * chars
|
berghofe@41809
|
29 |
val percent_comment: chars -> T * chars
|
berghofe@41809
|
30 |
val vcg_header: chars -> (banner * (date * time) option) * chars
|
berghofe@41809
|
31 |
val siv_header: chars ->
|
berghofe@41809
|
32 |
(banner * (date * time) * (date * time) * (string * string)) * chars
|
berghofe@41809
|
33 |
end;
|
berghofe@41809
|
34 |
|
berghofe@41809
|
35 |
structure Fdl_Lexer: FDL_LEXER =
|
berghofe@41809
|
36 |
struct
|
berghofe@41809
|
37 |
|
berghofe@41809
|
38 |
(** tokens **)
|
berghofe@41809
|
39 |
|
berghofe@41809
|
40 |
datatype kind = Keyword | Ident | Long_Ident | Traceability | Number | Comment | EOF;
|
berghofe@41809
|
41 |
|
berghofe@41809
|
42 |
datatype T = Token of kind * string * Position.T;
|
berghofe@41809
|
43 |
|
berghofe@41809
|
44 |
fun make_token k xs = Token (k, implode (map fst xs),
|
berghofe@41809
|
45 |
case xs of [] => Position.none | (_, p) :: _ => p);
|
berghofe@41809
|
46 |
|
berghofe@41809
|
47 |
fun kind_of (Token (k, _, _)) = k;
|
berghofe@41809
|
48 |
|
berghofe@41809
|
49 |
fun is_proper (Token (Comment, _, _)) = false
|
berghofe@41809
|
50 |
| is_proper _ = true;
|
berghofe@41809
|
51 |
|
berghofe@41809
|
52 |
fun content_of (Token (_, s, _)) = s;
|
berghofe@41809
|
53 |
|
berghofe@41809
|
54 |
fun unparse (Token (Traceability, s, _)) = "For " ^ s ^ ":"
|
berghofe@41809
|
55 |
| unparse (Token (_, s, _)) = s;
|
berghofe@41809
|
56 |
|
berghofe@41809
|
57 |
fun position_of (Token (_, _, pos)) = pos;
|
berghofe@41809
|
58 |
|
berghofe@41809
|
59 |
val pos_of = Position.str_of o position_of;
|
berghofe@41809
|
60 |
|
berghofe@41809
|
61 |
fun is_eof (Token (EOF, _, _)) = true
|
berghofe@41809
|
62 |
| is_eof _ = false;
|
berghofe@41809
|
63 |
|
berghofe@41809
|
64 |
fun mk_eof pos = Token (EOF, "", pos);
|
berghofe@41809
|
65 |
val eof = mk_eof Position.none;
|
berghofe@41809
|
66 |
|
berghofe@41809
|
67 |
val stopper =
|
berghofe@41809
|
68 |
Scan.stopper (fn [] => eof | toks => mk_eof (position_of (List.last toks))) is_eof;
|
berghofe@41809
|
69 |
|
berghofe@41809
|
70 |
fun leq_token (Token (_, s, _), Token (_, s', _)) = size s <= size s';
|
berghofe@41809
|
71 |
|
berghofe@41809
|
72 |
|
berghofe@41809
|
73 |
(** split up a string into a list of characters (with positions) **)
|
berghofe@41809
|
74 |
|
berghofe@41809
|
75 |
type chars = (string * Position.T) list;
|
berghofe@41809
|
76 |
|
berghofe@41809
|
77 |
fun is_char_eof ("", _) = true
|
berghofe@41809
|
78 |
| is_char_eof _ = false;
|
berghofe@41809
|
79 |
|
berghofe@41809
|
80 |
val char_stopper = Scan.stopper (K ("", Position.none)) is_char_eof;
|
berghofe@41809
|
81 |
|
berghofe@41809
|
82 |
fun symbol (x : string, _ : Position.T) = x;
|
berghofe@41809
|
83 |
|
berghofe@41809
|
84 |
fun explode_pos s pos = fst (fold_map (fn x => fn pos =>
|
berghofe@41832
|
85 |
((x, pos), Position.advance x pos)) (raw_explode s) pos);
|
berghofe@41809
|
86 |
|
berghofe@41809
|
87 |
|
berghofe@41809
|
88 |
(** scanners **)
|
berghofe@41809
|
89 |
|
berghofe@41809
|
90 |
val any = Scan.one (not o Scan.is_stopper char_stopper);
|
berghofe@41809
|
91 |
|
berghofe@41809
|
92 |
fun prfx [] = Scan.succeed []
|
berghofe@41809
|
93 |
| prfx (x :: xs) = Scan.one (equal x o symbol) ::: prfx xs;
|
berghofe@41809
|
94 |
|
berghofe@41809
|
95 |
val $$$ = prfx o raw_explode;
|
berghofe@41809
|
96 |
|
berghofe@41809
|
97 |
val lexicon = Scan.make_lexicon (map raw_explode
|
berghofe@41809
|
98 |
["rule_family",
|
berghofe@41809
|
99 |
"title",
|
berghofe@41809
|
100 |
"For",
|
berghofe@41809
|
101 |
":",
|
berghofe@41809
|
102 |
"[",
|
berghofe@41809
|
103 |
"]",
|
berghofe@41809
|
104 |
"(",
|
berghofe@41809
|
105 |
")",
|
berghofe@41809
|
106 |
",",
|
berghofe@41809
|
107 |
"&",
|
berghofe@41809
|
108 |
";",
|
berghofe@41809
|
109 |
"=",
|
berghofe@41809
|
110 |
".",
|
berghofe@41809
|
111 |
"..",
|
berghofe@41809
|
112 |
"requires",
|
berghofe@41809
|
113 |
"may_be_replaced_by",
|
berghofe@41809
|
114 |
"may_be_deduced",
|
berghofe@41809
|
115 |
"may_be_deduced_from",
|
berghofe@41809
|
116 |
"are_interchangeable",
|
berghofe@41809
|
117 |
"if",
|
berghofe@41809
|
118 |
"end",
|
berghofe@41809
|
119 |
"function",
|
berghofe@41809
|
120 |
"procedure",
|
berghofe@41809
|
121 |
"type",
|
berghofe@41809
|
122 |
"var",
|
berghofe@41809
|
123 |
"const",
|
berghofe@41809
|
124 |
"array",
|
berghofe@41809
|
125 |
"record",
|
berghofe@41809
|
126 |
":=",
|
berghofe@41809
|
127 |
"of",
|
berghofe@41809
|
128 |
"**",
|
berghofe@41809
|
129 |
"*",
|
berghofe@41809
|
130 |
"/",
|
berghofe@41809
|
131 |
"div",
|
berghofe@41809
|
132 |
"mod",
|
berghofe@41809
|
133 |
"+",
|
berghofe@41809
|
134 |
"-",
|
berghofe@41809
|
135 |
"<>",
|
berghofe@41809
|
136 |
"<",
|
berghofe@41809
|
137 |
">",
|
berghofe@41809
|
138 |
"<=",
|
berghofe@41809
|
139 |
">=",
|
berghofe@41809
|
140 |
"<->",
|
berghofe@41809
|
141 |
"->",
|
berghofe@41809
|
142 |
"not",
|
berghofe@41809
|
143 |
"and",
|
berghofe@41809
|
144 |
"or",
|
berghofe@41809
|
145 |
"for_some",
|
berghofe@41809
|
146 |
"for_all",
|
berghofe@41809
|
147 |
"***",
|
berghofe@41809
|
148 |
"!!!",
|
berghofe@41809
|
149 |
"element",
|
berghofe@41809
|
150 |
"update",
|
berghofe@41809
|
151 |
"pending"]);
|
berghofe@41809
|
152 |
|
berghofe@41809
|
153 |
fun keyword s = Scan.literal lexicon :|--
|
berghofe@41809
|
154 |
(fn xs => if map symbol xs = raw_explode s then Scan.succeed xs else Scan.fail);
|
berghofe@41809
|
155 |
|
berghofe@41809
|
156 |
fun is_digit x = "0" <= x andalso x <= "9";
|
berghofe@41809
|
157 |
fun is_alpha x = "a" <= x andalso x <= "z" orelse "A" <= x andalso x <= "Z";
|
berghofe@41809
|
158 |
val is_underscore = equal "_";
|
berghofe@41809
|
159 |
val is_tilde = equal "~";
|
berghofe@41809
|
160 |
val is_newline = equal "\n";
|
berghofe@41809
|
161 |
val is_tab = equal "\t";
|
berghofe@41809
|
162 |
val is_space = equal " ";
|
berghofe@41809
|
163 |
val is_whitespace = is_space orf is_tab orf is_newline;
|
berghofe@41809
|
164 |
val is_whitespace' = is_space orf is_tab;
|
berghofe@41809
|
165 |
|
berghofe@41809
|
166 |
val number = Scan.many1 (is_digit o symbol);
|
berghofe@41809
|
167 |
|
berghofe@41809
|
168 |
val identifier =
|
berghofe@41809
|
169 |
Scan.one (is_alpha o symbol) :::
|
berghofe@41809
|
170 |
Scan.many
|
berghofe@41809
|
171 |
((is_alpha orf is_digit orf is_underscore) o symbol) @@@
|
berghofe@41809
|
172 |
Scan.optional (Scan.one (is_tilde o symbol) >> single) [];
|
berghofe@41809
|
173 |
|
berghofe@41809
|
174 |
val long_identifier =
|
berghofe@41809
|
175 |
identifier @@@ (Scan.repeat1 ($$$ "." @@@ identifier) >> flat);
|
berghofe@41809
|
176 |
|
berghofe@41809
|
177 |
val whitespace = Scan.many (is_whitespace o symbol);
|
berghofe@41809
|
178 |
val whitespace' = Scan.many (is_whitespace' o symbol);
|
berghofe@41809
|
179 |
val newline = Scan.one (is_newline o symbol);
|
berghofe@41809
|
180 |
|
berghofe@41809
|
181 |
fun beginning n cs =
|
berghofe@41809
|
182 |
let
|
berghofe@41809
|
183 |
val drop_blanks = #1 o take_suffix is_whitespace;
|
berghofe@41809
|
184 |
val all_cs = drop_blanks cs;
|
berghofe@41809
|
185 |
val dots = if length all_cs > n then " ..." else "";
|
berghofe@41809
|
186 |
in
|
berghofe@41809
|
187 |
(drop_blanks (take n all_cs)
|
berghofe@41809
|
188 |
|> map (fn c => if is_whitespace c then " " else c)
|
berghofe@41809
|
189 |
|> implode) ^ dots
|
berghofe@41809
|
190 |
end;
|
berghofe@41809
|
191 |
|
berghofe@41809
|
192 |
fun !!! text scan =
|
berghofe@41809
|
193 |
let
|
berghofe@41809
|
194 |
fun get_pos [] = " (past end-of-text!)"
|
berghofe@41809
|
195 |
| get_pos ((_, pos) :: _) = Position.str_of pos;
|
berghofe@41809
|
196 |
|
wenzelm@44818
|
197 |
fun err (syms, msg) = fn () =>
|
berghofe@41809
|
198 |
text ^ get_pos syms ^ " at " ^ beginning 10 (map symbol syms) ^
|
wenzelm@44818
|
199 |
(case msg of NONE => "" | SOME m => "\n" ^ m ());
|
berghofe@41809
|
200 |
in Scan.!! err scan end;
|
berghofe@41809
|
201 |
|
berghofe@41809
|
202 |
val any_line' =
|
berghofe@41809
|
203 |
Scan.many (not o (Scan.is_stopper char_stopper orf (is_newline o symbol)));
|
berghofe@41809
|
204 |
|
berghofe@41809
|
205 |
val any_line = whitespace' |-- any_line' --|
|
berghofe@41809
|
206 |
newline >> (implode o map symbol);
|
berghofe@41809
|
207 |
|
berghofe@41809
|
208 |
fun gen_comment a b = $$$ a |-- !!! "missing end of comment"
|
berghofe@41809
|
209 |
(Scan.repeat (Scan.unless ($$$ b) any) --| $$$ b) >> make_token Comment;
|
berghofe@41809
|
210 |
|
berghofe@41809
|
211 |
val c_comment = gen_comment "/*" "*/";
|
berghofe@41809
|
212 |
val curly_comment = gen_comment "{" "}";
|
berghofe@41809
|
213 |
|
berghofe@41809
|
214 |
val percent_comment = $$$ "%" |-- any_line' >> make_token Comment;
|
berghofe@41809
|
215 |
|
berghofe@41809
|
216 |
fun repeatn 0 _ = Scan.succeed []
|
berghofe@41809
|
217 |
| repeatn n p = Scan.one p ::: repeatn (n-1) p;
|
berghofe@41809
|
218 |
|
berghofe@41809
|
219 |
|
berghofe@41809
|
220 |
(** header of *.vcg and *.siv files (see simplifier/load_provenance.pro) **)
|
berghofe@41809
|
221 |
|
berghofe@41809
|
222 |
type banner = string * string * string;
|
berghofe@41809
|
223 |
type date = string * string * string;
|
berghofe@41809
|
224 |
type time = string * string * string * string option;
|
berghofe@41809
|
225 |
|
berghofe@41809
|
226 |
val asterisks = Scan.repeat1 (Scan.one (equal "*" o symbol));
|
berghofe@41809
|
227 |
|
berghofe@41809
|
228 |
fun alphan n = repeatn n (is_alpha o symbol) >> (implode o map symbol);
|
berghofe@41809
|
229 |
fun digitn n = repeatn n (is_digit o symbol) >> (implode o map symbol);
|
berghofe@41809
|
230 |
|
berghofe@41809
|
231 |
val time =
|
berghofe@41809
|
232 |
digitn 2 --| $$$ ":" -- digitn 2 --| $$$ ":" -- digitn 2 --
|
berghofe@41809
|
233 |
Scan.option ($$$ "." |-- digitn 2) >>
|
berghofe@41809
|
234 |
(fn (((hr, mi), s), ms) => (hr, mi, s, ms));
|
berghofe@41809
|
235 |
|
berghofe@41809
|
236 |
val date =
|
berghofe@41809
|
237 |
digitn 2 --| $$$ "-" -- alphan 3 --| $$$ "-" -- digitn 4 >>
|
berghofe@41809
|
238 |
(fn ((d, m), y) => (d, m, y));
|
berghofe@41809
|
239 |
|
berghofe@41809
|
240 |
val banner =
|
berghofe@41809
|
241 |
whitespace' |-- asterisks --| whitespace' --| newline :|-- (fn xs =>
|
berghofe@41809
|
242 |
(any_line -- any_line -- any_line >>
|
berghofe@41809
|
243 |
(fn ((l1, l2), l3) => (l1, l2, l3))) --|
|
berghofe@41809
|
244 |
whitespace' --| prfx (map symbol xs) --| whitespace' --| newline);
|
berghofe@41809
|
245 |
|
berghofe@41809
|
246 |
val vcg_header = banner -- Scan.option (whitespace |--
|
berghofe@41809
|
247 |
$$$ "DATE :" |-- whitespace |-- date --| whitespace --|
|
berghofe@41809
|
248 |
Scan.option ($$$ "TIME :" -- whitespace) -- time);
|
berghofe@41809
|
249 |
|
berghofe@41809
|
250 |
val siv_header = banner --| whitespace --
|
berghofe@41809
|
251 |
($$$ "CREATED" |-- whitespace |-- (date --| $$$ "," --| whitespace -- time)) --|
|
berghofe@41809
|
252 |
whitespace --
|
berghofe@41809
|
253 |
($$$ "SIMPLIFIED" |-- whitespace |-- (date --| $$$ "," --| whitespace -- time)) --|
|
berghofe@41809
|
254 |
newline --| newline -- (any_line -- any_line) >>
|
berghofe@41809
|
255 |
(fn (((b, c), s), ls) => (b, c, s, ls));
|
berghofe@41809
|
256 |
|
berghofe@41809
|
257 |
|
berghofe@41809
|
258 |
(** the main tokenizer **)
|
berghofe@41809
|
259 |
|
berghofe@41809
|
260 |
fun scan header comment =
|
berghofe@41809
|
261 |
!!! "bad header" header --| whitespace --
|
berghofe@41809
|
262 |
Scan.repeat (Scan.unless (Scan.one is_char_eof)
|
berghofe@41809
|
263 |
(!!! "bad input"
|
berghofe@41809
|
264 |
( comment
|
berghofe@41809
|
265 |
|| (keyword "For" -- whitespace) |--
|
berghofe@41809
|
266 |
Scan.repeat1 (Scan.unless (keyword ":") any) --|
|
berghofe@41809
|
267 |
keyword ":" >> make_token Traceability
|
berghofe@41809
|
268 |
|| Scan.max leq_token
|
berghofe@41809
|
269 |
(Scan.literal lexicon >> make_token Keyword)
|
berghofe@41809
|
270 |
( long_identifier >> make_token Long_Ident
|
berghofe@41809
|
271 |
|| identifier >> make_token Ident)
|
berghofe@41809
|
272 |
|| number >> make_token Number) --|
|
berghofe@41809
|
273 |
whitespace));
|
berghofe@41809
|
274 |
|
berghofe@41809
|
275 |
fun tokenize header comment pos s =
|
berghofe@41809
|
276 |
fst (Scan.finite char_stopper
|
berghofe@41809
|
277 |
(Scan.error (scan header comment)) (explode_pos s pos));
|
berghofe@41809
|
278 |
|
berghofe@41809
|
279 |
end;
|