flitter/lang_cpp/parsing/token_views_cpp.ml
Joey Yakimowich-Payne fa600b98f7 Add poc files
2018-05-26 10:55:38 +09:00

665 lines
21 KiB
OCaml

(* Yoann Padioleau
*
* Copyright (C) 2011, 2014 Facebook
* Copyright (C) 2007, 2008 Ecole des Mines de Nantes
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License (GPL)
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* file license.txt for more details.
*)
open Common
module Flag = Flag_parsing_cpp
module PI = Parse_info
module TH = Token_helpers_cpp
open Parser_cpp
(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(*
* This module makes it easier to write some fuzzy parsing heuristics
* by offering different "views" over the same set of tokens.
*
* Normally I should not use ref/mutable in the token_extended type below
* and instead have a set of functions taking a list of tokens and
* returning a list of tokens. The problem is that to make easier some
* functions, it is better to work on better representation, on "views"
* over this list of tokens. But then modifying those views and get
* back from those views to the original simple list of tokens is
* tedious. One way is to maintain next to the view a list of "actions"
* (I was using a hash storing the charpos of the token and associating
* the action) but it is tedious too. Simpler to use mutable/ref. We
* use the same idea that we use when working on the Ast.
*
* old: when I was using the list of "actions" next to the views, the hash
* indexed by the charpos, there could have been some problems:
* how my fake_pos interact with the way I tag and adjust token ?
* because I base my tagging on the position of the token ! so sometimes
* could tag another fakeInfo that should not be tagged ?
* fortunately I don't use anymore this technique.
*)
(*****************************************************************************)
(* Some debugging functions *)
(*****************************************************************************)
let pr2, _pr2_once = Common2.mk_pr2_wrappers Flag.verbose_parsing
(*****************************************************************************)
(* Types *)
(*****************************************************************************)
type token_extended = {
(* chose 't' and not 'tok' to have a short name because we will write
* lots of ocaml patterns around this ... so better to be short
*)
mutable t: Parser_cpp.token;
(* In C++ we have functions inside classes, so need a stack of context *)
mutable where: context list;
(* less: need also a after ? *)
mutable new_tokens_before : Parser_cpp.token list;
(* line x col cache (more easily accessible) of the info in the token *)
line: int;
col : int;
}
(* The strategy to tag is mostly to look at the token(s) before the '{' *)
and context =
| InTopLevel
| InClassStruct of string (* can be __anon__ *) | InEnum
| InInitializer
| InAssign
| InParameter | InArgument
(* TODO actually commented in token_view_context because of c++ *)
| InFunction
(*
| InTemplateParam (* TODO *)
*)
(* InCondition ? InParenExpr ? *)
(* x list list, because x list separated by ',' *)
type paren_grouped =
| Parenthised of paren_grouped list list * token_extended list
| PToken of token_extended
type brace_grouped =
| Braceised of
brace_grouped list list * token_extended * token_extended option
| BToken of token_extended
(* Far better data structure than doing hacks in the lexer or parser
* because in lexer we don't know to which ifdef a endif is related
* and so when we want to comment a ifdef, we don't know which endif
* we must also comment. Especially true for the #if 0 which sometimes
* have a #else part.
*
* x list list, because x list separated by #else or #elif
*)
type ifdef_grouped =
| Ifdef of ifdef_grouped list list * token_extended list
| Ifdefbool of bool * ifdef_grouped list list * token_extended list
| NotIfdefLine of token_extended list
type 'a line_grouped =
Line of 'a list
type body_function_grouped =
| BodyFunction of token_extended list
| NotBodyLine of token_extended list
(* quite similar to ast_fuzzy.ml but with extended token *)
type multi_grouped =
| Braces of token_extended * multi_grouped list * token_extended option
| Parens of token_extended * multi_grouped list * token_extended option
| Angle of token_extended * multi_grouped list * token_extended option
| Tok of token_extended
(* with tarzan *)
exception UnclosedSymbol of string
(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)
let mk_token_extended x =
let info = TH.info_of_tok x in
let (line, col) = PI.line_of_info info, PI.col_of_info info in
{ t = x;
line = line; col = col;
(* we use List.hd at a few places, so convenient to have a sentinel *)
where = [InTopLevel];
new_tokens_before = [];
}
let mk_token_fake x =
{ t = x;
line = -1; col = -1;
where = [InTopLevel];
new_tokens_before = [];
}
let rebuild_tokens_extented toks_ext =
let _tokens = ref [] in
toks_ext +> List.iter (fun tok ->
tok.new_tokens_before +> List.iter (fun x -> push x _tokens);
push tok.t _tokens
);
let tokens = List.rev !_tokens in
(tokens +> Common2.acc_map mk_token_extended)
(*****************************************************************************)
(* View builders *)
(*****************************************************************************)
(* ------------------------------------------------------------------------- *)
(* Parens *)
(* ------------------------------------------------------------------------- *)
(* todo: synchro ! use more indentation
* if paren not closed and same indentation level, certainly because
* part of a mid-ifdef-expression.
*
* c++ext: TODO: need to handle templates here.
* The parenthized view must not consider the ',' in expressions
* like foo(lexical cast<string,int>, ...) as a separator for the arguments
* of foo(), otherwise we will get [lexical_cast<string; ...] which
* could confuse some heuristics.
*
* pre: have done the TInf->TInf_Template translation.
*)
let rec mk_parenthised xs =
match xs with
| [] -> []
| x::xs ->
(match x.t with
| xx when TH.is_opar xx ->
let body, extras, xs = mk_parameters [x] [] xs in
Parenthised (body,extras)::mk_parenthised xs
| _ ->
PToken x::mk_parenthised xs
)
(* return the body of the parenthised expression and the rest of the tokens *)
and mk_parameters extras acc_before_sep xs =
match xs with
| [] ->
(* maybe because of #ifdef which "opens" '(' in 2 branches *)
pr2 "PB: not found closing paren in fuzzy parsing";
[List.rev acc_before_sep], List.rev extras, []
| x::xs ->
(match x.t with
(* synchro *)
| xx when TH.is_obrace xx && x.col = 0 ->
pr2 "PB: found synchro point } in paren";
[List.rev acc_before_sep], List.rev (extras), (x::xs)
| xx when TH.is_cpar xx ->
[List.rev acc_before_sep], List.rev (x::extras), xs
| xx when TH.is_opar xx ->
let body, extrasnest, xs = mk_parameters [x] [] xs in
mk_parameters extras
(Parenthised (body,extrasnest)::acc_before_sep)
xs
| TComma _ ->
let body, extras, xs = mk_parameters (x::extras) [] xs in
(List.rev acc_before_sep)::body, extras, xs
| _ ->
mk_parameters extras (PToken x::acc_before_sep) xs
)
(* ------------------------------------------------------------------------- *)
(* Brace *)
(* ------------------------------------------------------------------------- *)
let rec mk_braceised xs =
match xs with
| [] -> []
| x::xs ->
(match x.t with
| xx when TH.is_obrace xx ->
let body, endbrace, xs = mk_braceised_aux [] xs in
Braceised (body, x, endbrace)::mk_braceised xs
| xx when TH.is_cbrace xx ->
pr2 "PB: found closing brace alone in fuzzy parsing";
BToken x::mk_braceised xs
| _ ->
BToken x::mk_braceised xs
)
(* return the body of the parenthised expression and the rest of the tokens *)
and mk_braceised_aux acc xs =
match xs with
| [] ->
(* maybe because of #ifdef which "opens" '(' in 2 branches *)
pr2 "PB: not found closing brace in fuzzy parsing";
[List.rev acc], None, []
| x::xs ->
(match x.t with
| xx when TH.is_cbrace xx -> [List.rev acc], Some x, xs
| xx when TH.is_obrace xx ->
let body, endbrace, xs = mk_braceised_aux [] xs in
mk_braceised_aux (Braceised (body,x, endbrace)::acc) xs
| _ ->
mk_braceised_aux (BToken x::acc) xs
)
(* ------------------------------------------------------------------------- *)
(* Ifdefs *)
(* ------------------------------------------------------------------------- *)
let rec mk_ifdef xs =
match xs with
| [] -> []
| x::xs ->
(match x.t with
| TIfdef _ ->
let body, extra, xs = mk_ifdef_parameters [x] [] xs in
Ifdef (body, extra)::mk_ifdef xs
| TIfdefBool (b,_) ->
let body, extra, xs = mk_ifdef_parameters [x] [] xs in
(* if not passing, then consider a #if 0 as an ordinary #ifdef *)
if !Flag.if0_passing
then Ifdefbool (b, body, extra)::mk_ifdef xs
else Ifdef(body, extra)::mk_ifdef xs
| TIfdefMisc (b,_) | TIfdefVersion (b,_) ->
let body, extra, xs = mk_ifdef_parameters [x] [] xs in
Ifdefbool (b, body, extra)::mk_ifdef xs
| _ ->
(* todo? can have some Ifdef in the line ? *)
let line, xs = Common.span (fun y -> y.line = x.line) (x::xs) in
NotIfdefLine line::mk_ifdef xs
)
and mk_ifdef_parameters extras acc_before_sep xs =
match xs with
| [] ->
(* Note that mk_ifdef is assuming that CPP instruction are alone
* on their line. Because I do a span (fun x -> is_same_line ...)
* I might take with me a #endif if this one is mixed on a line
* with some "normal" tokens.
*)
pr2 "PB: not found closing ifdef in fuzzy parsing";
[List.rev acc_before_sep], List.rev extras, []
| x::xs ->
(match x.t with
| TEndif _ ->
[List.rev acc_before_sep], List.rev (x::extras), xs
| TIfdef _ ->
let body, extrasnest, xs = mk_ifdef_parameters [x] [] xs in
mk_ifdef_parameters
extras (Ifdef (body, extrasnest)::acc_before_sep) xs
| TIfdefBool (b,_) ->
let body, extrasnest, xs = mk_ifdef_parameters [x] [] xs in
if !Flag.if0_passing
then
mk_ifdef_parameters
extras (Ifdefbool (b, body, extrasnest)::acc_before_sep) xs
else
mk_ifdef_parameters
extras (Ifdef (body, extrasnest)::acc_before_sep) xs
| TIfdefMisc (b,_) | TIfdefVersion (b,_) ->
let body, extrasnest, xs = mk_ifdef_parameters [x] [] xs in
mk_ifdef_parameters
extras (Ifdefbool (b, body, extrasnest)::acc_before_sep) xs
| TIfdefelse _
| TIfdefelif _ ->
let body, extras, xs = mk_ifdef_parameters (x::extras) [] xs in
(List.rev acc_before_sep)::body, extras, xs
| _ ->
let line, xs = Common.span (fun y -> y.line = x.line) (x::xs) in
mk_ifdef_parameters extras (NotIfdefLine line::acc_before_sep) xs
)
(* ------------------------------------------------------------------------- *)
(* Lines (of parens) *)
(* ------------------------------------------------------------------------- *)
let line_of_paren = function
| PToken x -> x.line
| Parenthised (_xxs, info_parens) ->
(match info_parens with
| [] -> raise Impossible
| x::_xs -> x.line
)
(* old
let rec span_line_paren line = function
| [] -> [],[]
| x::xs ->
(match x with
| PToken tok when TH.is_eof tok.t ->
[], x::xs
| _ ->
if line_of_paren x = line
then
let (l1, l2) = span_line_paren line xs in
(x::l1, l2)
else ([], x::xs)
)
let rec mk_line_parenthised xs =
match xs with
| [] -> []
| x::xs ->
let line_no = line_of_paren x in
let line, xs = span_line_paren line_no xs in
Line (x::line)::mk_line_parenthised xs
*)
let line_range_of_paren = function
| PToken x -> x.line, x.line
| Parenthised (_xxs, info_parens) ->
(match info_parens with
| [] -> raise Impossible
| x::xs ->
let lines_no = (x::xs) +> List.map (fun x -> x.line) in
Common2.minimum lines_no, Common2.maximum lines_no
)
let rec span_line_paren_range (imin, imax) = function
| [] -> [],[]
| x::xs ->
(match x with
| PToken tok when TH.is_eof tok.t ->
[], x::xs
| _ ->
if line_of_paren x >= imin && line_of_paren x <= imax
then
(* may need to extend *)
let (_imin', imax') = line_range_of_paren x in
let (l1, l2) = span_line_paren_range (imin, max imax imax') xs in
(x::l1, l2)
else ([], x::xs)
)
let rec mk_line_parenthised xs =
match xs with
| [] -> []
| x::xs ->
let line_range = line_range_of_paren x in
let line, xs = span_line_paren_range line_range xs in
Line (x::line)::mk_line_parenthised xs
(* ------------------------------------------------------------------------- *)
(* Function body *)
(* ------------------------------------------------------------------------- *)
let rec mk_body_function_grouped xs =
match xs with
| [] -> []
| x::xs ->
(match x with
| {t=TOBrace _; col = 0; _} ->
let is_closing_brace = function
| {t = TCBrace _; col = 0; _ } -> true
| _ -> false
in
let body, xs = Common.span (fun x -> not (is_closing_brace x)) xs in
(match xs with
| ({t = TCBrace _; col = 0; _ })::xs ->
BodyFunction body::mk_body_function_grouped xs
| [] ->
pr2 "PB:not found closing brace in fuzzy parsing";
[NotBodyLine body]
| _ -> raise Impossible
)
| _ ->
let line, xs = Common.span (fun y -> y.line = x.line) (x::xs) in
NotBodyLine line::mk_body_function_grouped xs
)
(* ------------------------------------------------------------------------- *)
(* Multi ('{', '(', '<') (could also do '[' ?) *)
(* ------------------------------------------------------------------------- *)
(* Assumes work on a list of tokens without comments, without ifdefs
* (todo? and without #define?).
* Used for typedef inference. Now also used for fuzzy parsing!
*
* todo? more fault tolerance, if col == 0 and { the reset!
* less: could check that it's consistent with the indentation
*
*)
let mk_multi xs =
let rec consume x xs =
match x with
| {t=(*TOBrace ii*)tok;_} when TH.is_obrace tok ->
let body, closing, rest = look_close_brace x [] xs in
Braces (x, body, closing), rest
| {t=(*TOPar ii*)tok;_} when TH.is_opar tok ->
let body, closing, rest = look_close_paren x [] xs in
Parens (x, body, closing), rest
| {t=TInf_Template _ii;_} ->
let body, closing, rest = look_close_template x [] xs in
Angle (x, body, closing), rest
| x -> Tok x, xs
and aux xs =
match xs with
| [] -> []
| x::xs ->
let x', xs' = consume x xs in
x'::aux xs'
and look_close_brace tok_start accbody xs =
match xs with
| [] ->
raise (UnclosedSymbol (spf "PB look_close_brace (started at %d)"
(TH.line_of_tok tok_start.t)))
| x::xs ->
(match x with
| {t=TCBrace _ii;_} -> List.rev accbody, Some x, xs
(* Many macros have unclosed '{'. An alternative
* would be to work on a view where define has been filtered
*)
| {t=TCommentNewline_DefineEndOfMacro _ii;_} ->
List.rev accbody, None, x::xs
| _ -> let (x', xs') = consume x xs in
look_close_brace tok_start (x'::accbody) xs'
)
and look_close_paren tok_start accbody xs =
match xs with
| [] ->
raise (UnclosedSymbol (spf "PB look_close_paren (started at %d)"
(TH.line_of_tok tok_start.t)))
| x::xs ->
(match x with
| {t=(*TCPar ii*)tok;_} when TH.is_cpar tok ->
List.rev accbody, Some x, xs
| _ ->
let (x', xs') = consume x xs in
look_close_paren tok_start (x'::accbody) xs'
)
and look_close_template tok_start accbody xs =
match xs with
| [] ->
raise (UnclosedSymbol (spf "PB look_close_template (started at %d)"
(TH.line_of_tok tok_start.t)))
| x::xs ->
(match x with
| {t=TSup_Template _ii;_} -> List.rev accbody, Some x, xs
| _ -> let (x', xs') = consume x xs in
look_close_template tok_start (x'::accbody) xs'
)
in
aux xs
let split_comma xs =
xs +> Common2.split_gen_when (function
| Tok{t=TComma _;_}::xs -> Some xs
| _ -> None
)
(*****************************************************************************)
(* View iterators *)
(*****************************************************************************)
let rec iter_token_paren f xs =
xs +> List.iter (function
| PToken tok -> f tok;
| Parenthised (xxs, info_parens) ->
info_parens +> List.iter f;
xxs +> List.iter (fun xs -> iter_token_paren f xs)
)
let rec iter_token_brace f xs =
xs +> List.iter (function
| BToken tok -> f tok;
| Braceised (xxs, tok1, tok2opt) ->
f tok1; do_option f tok2opt;
xxs +> List.iter (fun xs -> iter_token_brace f xs)
)
let rec iter_token_ifdef f xs =
xs +> List.iter (function
| NotIfdefLine xs -> xs +> List.iter f;
| Ifdefbool (_, xxs, info_ifdef)
| Ifdef (xxs, info_ifdef) ->
info_ifdef +> List.iter f;
xxs +> List.iter (iter_token_ifdef f)
)
let rec iter_token_multi f xs =
xs +> List.iter (function
| Tok t -> f t
| Braces (t1, xs, t2)
| Parens (t1, xs, t2)
| Angle (t1, xs, t2)
->
f t1;
iter_token_multi f xs;
Common.do_option f t2
)
let tokens_of_paren xs =
let g = ref [] in
xs +> iter_token_paren (fun tok -> push tok g);
List.rev !g
let tokens_of_paren_ordered xs =
let g = ref [] in
let rec aux_tokens_ordered = function
| PToken tok -> push tok g;
| Parenthised (xxs, info_parens) ->
let (opar, cpar, commas) =
match info_parens with
| opar::xs ->
(match List.rev xs with
| cpar::xs ->
opar, cpar, List.rev xs
| _ -> raise Impossible
)
| _ -> raise Impossible
in
push opar g;
aux_args (xxs,commas);
push cpar g;
and aux_args (xxs, commas) =
match xxs, commas with
| [], [] -> ()
| [xs], [] -> xs +> List.iter aux_tokens_ordered
| xs::ys::xxs, comma::commas ->
xs +> List.iter aux_tokens_ordered;
push comma g;
aux_args (ys::xxs, commas)
| _ -> raise Impossible
in
xs +> List.iter aux_tokens_ordered;
List.rev !g
let tokens_of_multi_grouped xs =
let res = ref [] in
let add x = Common.push x res in
let rec aux xs =
xs +> List.iter (function
| Tok t1 -> add t1
| Braces (t1, xs, t2)
| Parens (t1, xs, t2)
| Angle (t1, xs, t2) ->
add t1;
aux xs;
Common.do_option add t2
)
in
aux xs;
List.rev !res
(*****************************************************************************)
(* vof *)
(*****************************************************************************)
let vof_context = function
| InTopLevel -> Ocaml.VSum ("T", [])
| InClassStruct _s -> Ocaml.VSum ("C", [])
| InEnum -> Ocaml.VSum ("E", [])
| InInitializer -> Ocaml.VSum ("I", [])
| InAssign -> Ocaml.VSum ("=", [])
| InParameter -> Ocaml.VSum ("P", [])
| InArgument -> Ocaml.VSum ("A", [])
| InFunction -> Ocaml.VSum ("F", [])
(*
| InTemplateParam -> Ocaml.VSum ("<>", [])
*)
let vof_token_extended t =
let info = TH.info_of_tok t.t in
let str = PI.str_of_info info in
let xs = List.map vof_context t.where in
Ocaml.VTuple [Ocaml.VString str; Ocaml.VList xs]
let rec vof_multi_grouped =
function
| Braces ((v1, v2, v3)) ->
let v1 = vof_token_extended v1
and v2 = Ocaml.vof_list vof_multi_grouped v2
and v3 = Ocaml.vof_option vof_token_extended v3
in Ocaml.VSum (("Braces", [ v1; v2; v3 ]))
| Parens ((v1, v2, v3)) ->
let v1 = vof_token_extended v1
and v2 = Ocaml.vof_list vof_multi_grouped v2
and v3 = Ocaml.vof_option vof_token_extended v3
in Ocaml.VSum (("Parens", [ v1; v2; v3 ]))
| Angle ((v1, v2, v3)) ->
let v1 = vof_token_extended v1
and v2 = Ocaml.vof_list vof_multi_grouped v2
and v3 = Ocaml.vof_option vof_token_extended v3
in Ocaml.VSum (("Angle", [ v1; v2; v3 ]))
| Tok v1 -> let v1 = vof_token_extended v1 in Ocaml.VSum (("Tok", [ v1 ]))
let vof_multi_grouped_list xs =
let v = Ocaml.VList (xs +> List.map vof_multi_grouped) in
v