ocaml/lex/lexer.mll

300 lines
8.6 KiB
OCaml

(***********************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. All rights reserved. This file is distributed *)
(* under the terms of the Q Public License version 1.0. *)
(* *)
(***********************************************************************)
(* The lexical analyzer for lexer definitions. Bootstrapped! *)
{
open Syntax
open Parser
(* Auxiliaries for the lexical analyzer *)
let brace_depth = ref 0
and comment_depth = ref 0
let in_pattern () = !brace_depth = 0 && !comment_depth = 0
exception Lexical_error of string * string * int * int
let string_buff = Buffer.create 256
let reset_string_buffer () = Buffer.clear string_buff
let store_string_char c = Buffer.add_char string_buff c
let get_stored_string () = Buffer.contents string_buff
let char_for_backslash = function
'n' -> '\010'
| 'r' -> '\013'
| 'b' -> '\008'
| 't' -> '\009'
| c -> c
let raise_lexical_error lexbuf msg =
let p = Lexing.lexeme_start_p lexbuf in
raise (Lexical_error (msg,
p.Lexing.pos_fname,
p.Lexing.pos_lnum,
p.Lexing.pos_cnum - p.Lexing.pos_bol + 1))
;;
let handle_lexical_error fn lexbuf =
let p = Lexing.lexeme_start_p lexbuf in
let line = p.Lexing.pos_lnum
and column = p.Lexing.pos_cnum - p.Lexing.pos_bol + 1
and file = p.Lexing.pos_fname
in
try
fn lexbuf
with Lexical_error (msg, "", 0, 0) ->
raise(Lexical_error(msg, file, line, column))
let get_input_name () = Sys.argv.(Array.length Sys.argv - 1)
let warning lexbuf msg =
let p = Lexing.lexeme_start_p lexbuf in
Printf.eprintf "ocamllex warning:\nFile \"%s\", line %d, character %d: %s.\n"
p.Lexing.pos_fname p.Lexing.pos_lnum
(p.Lexing.pos_cnum - p.Lexing.pos_bol + 1) msg;
flush stderr
let decimal_code c d u =
100 * (Char.code c - 48) + 10 * (Char.code d - 48) + (Char.code u - 48)
let char_for_hexadecimal_code d u =
let d1 = Char.code d in
let val1 = if d1 >= 97 then d1 - 87
else if d1 >= 65 then d1 - 55
else d1 - 48
in
let d2 = Char.code u in
let val2 = if d2 >= 97 then d2 - 87
else if d2 >= 65 then d2 - 55
else d2 - 48
in
Char.chr (val1 * 16 + val2)
let incr_loc lexbuf delta =
let pos = lexbuf.Lexing.lex_curr_p in
lexbuf.Lexing.lex_curr_p <- { pos with
Lexing.pos_lnum = pos.Lexing.pos_lnum + 1;
Lexing.pos_bol = pos.Lexing.pos_cnum - delta;
}
;;
let update_loc lexbuf opt_file line =
let pos = lexbuf.Lexing.lex_curr_p in
let new_file = match opt_file with
| None -> pos.Lexing.pos_fname
| Some f -> f
in
lexbuf.Lexing.lex_curr_p <- { pos with
Lexing.pos_fname = new_file;
Lexing.pos_lnum = line;
Lexing.pos_bol = pos.Lexing.pos_cnum;
}
;;
}
let identstart =
['A'-'Z' 'a'-'z' '_' '\192'-'\214' '\216'-'\246' '\248'-'\255']
let identbody =
['A'-'Z' 'a'-'z' '_' '\192'-'\214' '\216'-'\246' '\248'-'\255' '\'' '0'-'9']
let backslash_escapes =
['\\' '\'' '"' 'n' 't' 'b' 'r' ' ']
rule main = parse
[' ' '\013' '\009' '\012' ] +
{ main lexbuf }
| '\010'
{ incr_loc lexbuf 0;
main lexbuf }
| "#" [' ' '\t']* (['0'-'9']+ as num) [' ' '\t']*
('\"' ([^ '\010' '\013' '\"']* as name) '\"')?
[^ '\010' '\013']* '\010'
{ update_loc lexbuf name (int_of_string num);
main lexbuf
}
| "(*"
{ comment_depth := 1;
handle_lexical_error comment lexbuf;
main lexbuf }
| '_' { Tunderscore }
| identstart identbody *
{ match Lexing.lexeme lexbuf with
"rule" -> Trule
| "parse" -> Tparse
| "shortest" -> Tparse_shortest
| "and" -> Tand
| "eof" -> Teof
| "let" -> Tlet
| "as" -> Tas
| s -> Tident s }
| '"'
{ reset_string_buffer();
handle_lexical_error string lexbuf;
Tstring(get_stored_string()) }
(* note: ''' is a valid character literal (by contrast with the compiler) *)
| "'" [^ '\\'] "'"
{ Tchar(Char.code(Lexing.lexeme_char lexbuf 1)) }
| "'" '\\' backslash_escapes "'"
{ Tchar(Char.code(char_for_backslash (Lexing.lexeme_char lexbuf 2))) }
| "'" '\\' (['0'-'9'] as c) (['0'-'9'] as d) (['0'-'9'] as u)"'"
{ let v = decimal_code c d u in
if v > 255 then
raise_lexical_error lexbuf
(Printf.sprintf "illegal escape sequence \\%c%c%c" c d u)
else
Tchar v }
| "'" '\\' 'x'
(['0'-'9' 'a'-'f' 'A'-'F'] as d) (['0'-'9' 'a'-'f' 'A'-'F'] as u) "'"
{ Tchar(Char.code(char_for_hexadecimal_code d u)) }
| "'" '\\' (_ as c)
{ raise_lexical_error lexbuf
(Printf.sprintf "illegal escape sequence \\%c" c)
}
| '{'
{ let p = Lexing.lexeme_end_p lexbuf in
let n1 = p.Lexing.pos_cnum
and l1 = p.Lexing.pos_lnum
and s1 = p.Lexing.pos_bol in
brace_depth := 1;
let n2 = handle_lexical_error action lexbuf in
Taction({start_pos = n1; end_pos = n2;
start_line = l1; start_col = n1 - s1}) }
| '=' { Tequal }
| '|' { Tor }
| '[' { Tlbracket }
| ']' { Trbracket }
| '*' { Tstar }
| '?' { Tmaybe }
| '+' { Tplus }
| '(' { Tlparen }
| ')' { Trparen }
| '^' { Tcaret }
| '-' { Tdash }
| '#' { Tsharp }
| eof { Tend }
| _
{ raise_lexical_error lexbuf
("illegal character " ^ String.escaped(Lexing.lexeme lexbuf))
}
(* String parsing comes from the compiler lexer *)
and string = parse
'"'
{ () }
| '\\' ("\010" | "\013" | "\013\010") ([' ' '\009'] * as spaces)
{ incr_loc lexbuf (String.length spaces);
string lexbuf }
| '\\' (backslash_escapes as c)
{ store_string_char(char_for_backslash c);
string lexbuf }
| '\\' (['0'-'9'] as c) (['0'-'9'] as d) (['0'-'9'] as u)
{ let v = decimal_code c d u in
if in_pattern () && v > 255 then
warning lexbuf
(Printf.sprintf
"illegal backslash escape in string: `\\%c%c%c'" c d u) ;
store_string_char (Char.chr v);
string lexbuf }
| '\\' 'x' (['0'-'9' 'a'-'f' 'A'-'F'] as d) (['0'-'9' 'a'-'f' 'A'-'F'] as u)
{ store_string_char (char_for_hexadecimal_code d u) ;
string lexbuf }
| '\\' (_ as c)
{if in_pattern () then
warning lexbuf
(Printf.sprintf "illegal backslash escape in string: `\\%c'" c) ;
store_string_char '\\' ;
store_string_char c ;
string lexbuf }
| eof
{ raise(Lexical_error("unterminated string", "", 0, 0)) }
| '\010'
{ store_string_char '\010';
incr_loc lexbuf 0;
string lexbuf }
| _ as c
{ store_string_char c;
string lexbuf }
(*
Lexers comment and action are quite similar,
they should lex both strings and characters,
in order not to be confused by what is inside then
*)
and comment = parse
"(*"
{ incr comment_depth; comment lexbuf }
| "*)"
{ decr comment_depth;
if !comment_depth = 0 then () else comment lexbuf }
| '"'
{ reset_string_buffer();
string lexbuf;
reset_string_buffer();
comment lexbuf }
| "'"
{ skip_char lexbuf ;
comment lexbuf }
| eof
{ raise(Lexical_error("unterminated comment", "", 0, 0)) }
| '\010'
{ incr_loc lexbuf 0;
comment lexbuf }
| _
{ comment lexbuf }
and action = parse
'{'
{ incr brace_depth;
action lexbuf }
| '}'
{ decr brace_depth;
if !brace_depth = 0 then Lexing.lexeme_start lexbuf else action lexbuf }
| '"'
{ reset_string_buffer();
handle_lexical_error string lexbuf;
reset_string_buffer();
action lexbuf }
| "'"
{ skip_char lexbuf ;
action lexbuf }
| "(*"
{ comment_depth := 1;
comment lexbuf;
action lexbuf }
| eof
{ raise (Lexical_error("unterminated action", "", 0, 0)) }
| '\010'
{ incr_loc lexbuf 0;
action lexbuf }
| _
{ action lexbuf }
and skip_char = parse
| '\\'? '\010' "'"
{ incr_loc lexbuf 1;
}
| [^ '\\' '\''] "'" (* regular character *)
(* one character and numeric escape sequences *)
| '\\' _ "'"
| '\\' ['0'-'9'] ['0'-'9'] ['0'-'9'] "'"
| '\\' 'x' ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'] "'"
{()}
(* A dieu va ! *)
| "" {()}