diff --git a/src/ocaml/preprocess/lexer_raw.mli b/src/ocaml/preprocess/lexer_raw.mli index 67965e90a..4f66ff0fe 100644 --- a/src/ocaml/preprocess/lexer_raw.mli +++ b/src/ocaml/preprocess/lexer_raw.mli @@ -23,6 +23,7 @@ type error = | Empty_character_literal | Keyword_as_label of string | Invalid_literal of string + | Unknown_keyword of string exception Error of error * Location.t (* Keywords, manipulated by extensions *) diff --git a/src/ocaml/preprocess/lexer_raw.mll b/src/ocaml/preprocess/lexer_raw.mll index 5764b541b..2c216ddc8 100644 --- a/src/ocaml/preprocess/lexer_raw.mll +++ b/src/ocaml/preprocess/lexer_raw.mll @@ -30,6 +30,7 @@ type error = | Empty_character_literal | Keyword_as_label of string | Invalid_literal of string + | Unknown_keyword of string exception Error of error * Location.t @@ -79,68 +80,97 @@ let rec catch m f = match m with (* The table of keywords *) -let keyword_table : keywords = - create_hashtable 149 [ - "and", AND; - "as", AS; - "assert", ASSERT; - "begin", BEGIN; - "class", CLASS; - "constraint", CONSTRAINT; - "do", DO; - "done", DONE; - "downto", DOWNTO; - "else", ELSE; - "end", END; - "exception", EXCEPTION; - "external", EXTERNAL; - "false", FALSE; - "for", FOR; - "fun", FUN; - "function", FUNCTION; - "functor", FUNCTOR; - "if", IF; - "in", IN; - "include", INCLUDE; - "inherit", INHERIT; - "initializer", INITIALIZER; - "lazy", LAZY; - "let", LET; - "match", MATCH; - "method", METHOD; - "module", MODULE; - "mutable", MUTABLE; - "new", NEW; - "nonrec", NONREC; - "object", OBJECT; - "of", OF; - "open", OPEN; - "or", OR; +let all_keywords = + let v5_3 = Some (5,3) in + let v1_0 = Some (1,0) in + let v1_6 = Some (1,6) in + let v4_2 = Some (4,2) in + let always = None in + [ + "and", AND, always; + "as", AS, always; + "assert", ASSERT, v1_6; + "begin", BEGIN, always; + "class", CLASS, v1_0; + "constraint", CONSTRAINT, v1_0; + "do", DO, always; + "done", DONE, always; + "downto", DOWNTO, always; + "effect", EFFECT, v5_3; + "else", ELSE, always; + "end", END, always; + "exception", EXCEPTION, always; + "external", EXTERNAL, always; + "false", FALSE, always; + "for", FOR, always; + "fun", FUN, always; + "function", FUNCTION, always; + "functor", FUNCTOR, always; + "if", IF, always; + "in", IN, always; + "include", INCLUDE, always; + "inherit", INHERIT, v1_0; + "initializer", INITIALIZER, v1_0; + "lazy", LAZY, v1_6; + "let", LET, always; + "match", MATCH, always; + "method", METHOD, v1_0; + "module", MODULE, always; + "mutable", MUTABLE, always; + "new", NEW, v1_0; + "nonrec", NONREC, v4_2; + "object", OBJECT, v1_0; + "of", OF, always; + "open", OPEN, always; + "or", OR, always; (* "parser", PARSER; *) - "private", PRIVATE; - "rec", REC; - "sig", SIG; - "struct", STRUCT; - "then", THEN; - "to", TO; - "true", TRUE; - "try", TRY; - "type", TYPE; - "val", VAL; - "virtual", VIRTUAL; - "when", WHEN; - "while", WHILE; - "with", WITH; - - "lor", INFIXOP3("lor"); (* Should be INFIXOP2 *) - "lxor", INFIXOP3("lxor"); (* Should be INFIXOP2 *) - "mod", INFIXOP3("mod"); - "land", INFIXOP3("land"); - "lsl", INFIXOP4("lsl"); - "lsr", INFIXOP4("lsr"); - "asr", INFIXOP4("asr"); + "private", PRIVATE, v1_0; + "rec", REC, always; + "sig", SIG, always; + "struct", STRUCT, always; + "then", THEN, always; + "to", TO, always; + "true", TRUE, always; + "try", TRY, always; + "type", TYPE, always; + "val", VAL, always; + "virtual", VIRTUAL, v1_0; + "when", WHEN, always; + "while", WHILE, always; + "with", WITH, always; + + "lor", INFIXOP3("lor"), always; (* Should be INFIXOP2 *) + "lxor", INFIXOP3("lxor"), always; (* Should be INFIXOP2 *) + "mod", INFIXOP3("mod"), always; + "land", INFIXOP3("land"), always; + "lsl", INFIXOP4("lsl"), always; + "lsr", INFIXOP4("lsr"), always; + "asr", INFIXOP4("asr"), always ] +let keyword_table = Hashtbl.create 149 + +let populate_keywords (version,keywords) = + let greater (x:(int*int) option) (y:(int*int) option) = + match x, y with + | None, _ | _, None -> true + | Some x, Some y -> x >= y + in + let tbl = keyword_table in + Hashtbl.clear tbl; + let add_keyword (name, token, since) = + if greater version since then Hashtbl.replace tbl name (Some token) + in + List.iter ~f:add_keyword all_keywords; + List.iter ~f:(fun name -> + match List.find ~f:(fun (n,_,_) -> n = name) all_keywords with + | (_,tok,_) -> Hashtbl.replace tbl name (Some tok) + | exception Not_found -> Hashtbl.replace tbl name None + ) keywords + +(* FIXME: Merlin: this could be made configurable *) +let () = populate_keywords (None,[]) + let keywords l = create_hashtable 11 l let list_keywords = @@ -288,12 +318,25 @@ let uchar_for_uchar_escape lexbuf = let keyword_or state s default = try Hashtbl.find state.keywords s - with Not_found -> try Hashtbl.find keyword_table s - with Not_found -> default + with Not_found -> + try Option.value ~default @@ Hashtbl.find keyword_table s + with Not_found -> default + +let is_keyword name = + Hashtbl.mem keyword_table name -let is_keyword name = Hashtbl.mem keyword_table name let () = Lexer.is_keyword_ref := is_keyword +let find_keyword lexbuf name default = + match Hashtbl.find keyword_table name with + | Some x -> return x + | None -> fail lexbuf (Unknown_keyword name) + | exception Not_found -> return default + +let find_keyword state lexbuf ~name ~default = + try return @@ Hashtbl.find state.keywords name + with Not_found -> find_keyword lexbuf name default + let check_label_name lexbuf name = if is_keyword name then fail lexbuf (Keyword_as_label name) @@ -358,6 +401,11 @@ let prepare_error loc = function "%a is a keyword, it cannot be used as label name" Style.inline_code kwd | Invalid_literal s -> Location.errorf ~loc "Invalid literal %s" s + | Unknown_keyword name -> + Location.errorf ~loc + "%a has been defined as an additional keyword.@ \ + This version of OCaml does not support this keyword." + Style.inline_code name (* FIXME: Invalid_directive? *) let () = @@ -470,20 +518,12 @@ rule token state = parse | raw_ident_escape (lowercase identchar * as name) { return (LIDENT name) } | lowercase identchar * as name - { return (try Hashtbl.find state.keywords name - with Not_found -> - try Hashtbl.find keyword_table name - with Not_found -> - LIDENT name) } + { (find_keyword state lexbuf ~name ~default:(LIDENT name)) } | lowercase_latin1 identchar_latin1 * as name { warn_latin1 lexbuf; return (LIDENT name) } | uppercase identchar * as name { (* Capitalized keywords for OUnit *) - return (try Hashtbl.find state.keywords name - with Not_found -> - try Hashtbl.find keyword_table name - with Not_found -> - UIDENT name) } + (find_keyword state lexbuf ~name ~default:(UIDENT name))} | uppercase_latin1 identchar_latin1 * as name { warn_latin1 lexbuf; return (UIDENT name) } | int_literal as lit { return (INT (lit, None)) }