src/HOL/SPARK/Tools/fdl_lexer.ML
author wenzelm
Sat, 23 Jul 2011 16:37:17 +0200
changeset 44818 9b00f09f7721
parent 41832 2b07a4212d6d
child 47949 d04b38d4035b
permissions -rw-r--r--
defer evaluation of Scan.message, for improved performance in the frequent situation where failure is handled later (e.g. via ||);
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;