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

235 lines
8.2 KiB
OCaml

(* Yoann Padioleau
*
* Copyright (C) 2007, 2008 Ecole des Mines de Nantes
* Copyright (C) 2011 Facebook
*
* 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 Ast = Ast_cpp
module Flag = Flag_parsing_cpp
module TH = Token_helpers_cpp
module Parser = Parser_cpp
module Hack = Parsing_hacks_lib
open Parser_cpp
open Token_views_cpp
(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(*
* CPP functions working at the token level. See pp_ast.ml for cpp functions
* working at the AST level (which is very unusual but makes sense in
* the coccinelle context for instance).
*
* Note that because I use a single lexer to work both at the C and cpp level
* there are some inconveniencies.
* For instance 'for' is a valid name for a macro parameter and macro
* body, but is interpreted in a special way by our single lexer, and
* so at some places where I expect a TIdent I need also to
* handle special cases and accept Tfor, Tif, etc at those places.
*
* There are multiple issues related to those keywords incorrect tokens.
* Those keywords can be:
*
* - (1) in the name of the macro as in #define inline
* - (2) in a parameter of the macro as in #define foo(char) char x;
* - (3) in an argument to a macro call as in IDENT(if);
*
* Case 1 is easy to fix in define_ident in ???
*
* Case 2 is easy to fix in define_parse below, where we detect such tokens
* in the parameters and then replace their occurence in the body with
* a TIdent.
*
* Case 3 is only an issue when the expanded token is not really used
* as usual but used for instance in concatenation as in a ## if
* when expanded. In the case the grammar this time will not be happy
* so this is also easy to fix in cpp_engine.
*)
(*****************************************************************************)
(* Wrappers *)
(*****************************************************************************)
let pr2, _pr2_once = Common2.mk_pr2_wrappers Flag_parsing_cpp.verbose_parsing
(*****************************************************************************)
(* Types *)
(*****************************************************************************)
(* the tokens in the body of the macro are all ExpandedTok *)
type define_body = (unit,string list) either * Parser_cpp.token list
(* TODO:
type define_def = string * define_param * define_body
and define_param =
| NoParam
| Params of string list
and define_body =
| DefineBody of Parser_c.token list
| DefineHint of parsinghack_hint
and parsinghack_hint =
| HintIterator
| HintDeclarator
| HintMacroString
| HintMacroStatement
| HintAttribute
| HintMacroIdentBuilder
*)
(*****************************************************************************)
(* Apply macro (using standard.h or other defs) *)
(*****************************************************************************)
(* cpp-builtin part1, macro, using standard.h or other defs *)
(* Thanks to this function many stuff are not anymore hardcoded in
* OCaml code (but are now hardcoded in standard.h ...)
*)
let (cpp_engine:
(string , Parser.token list) assoc -> Parser.token list -> Parser.token list)
= fun env xs ->
xs +> List.map (fun tok ->
match tok with
| TIdent (s,_i1) when List.mem_assoc s env -> Common2.assoc s env
| x -> [x]
)
+> List.flatten
(*
* We apply a macro by generating new ExpandedToken and by
* commenting the old macro call.
*
* no need to take care to substitute the macro name itself
* that occurs in the macro definition because the macro name is
* after fix_token_define a TDefineIdent, no more a TIdent.
*)
let apply_macro_defs defs xs =
let rec apply_macro_defs xs =
match xs with
| [] -> ()
(* recognized macro of standard.h (or other) *)
| PToken ({t=TIdent (s,_i1);_} as id)::Parenthised (xxs,info_parens)::xs
when Hashtbl.mem defs s ->
Hack.pr2_pp ("MACRO: found known macro = " ^ s);
(match Hashtbl.find defs s with
| Left (), bodymacro ->
pr2 ("macro without param used before parenthize, wierd: " ^ s);
(* ex: PRINTP("NCR53C400 card%s detected\n" ANDP(((struct ... *)
Hack.set_as_comment (Token_cpp.CppMacroExpanded) id;
id.new_tokens_before <- bodymacro;
| Right params, bodymacro ->
if List.length params = List.length xxs
then
let xxs' = xxs +> List.map (fun x ->
(tokens_of_paren_ordered x) +> List.map (fun x ->
TH.visitor_info_of_tok Ast.make_expanded x.t
)
) in
id.new_tokens_before <-
cpp_engine (Common2.zip params xxs') bodymacro
else begin
pr2 ("macro with wrong number of arguments, wierd: " ^ s);
id.new_tokens_before <- bodymacro;
end;
(* important to do that after have apply the macro, otherwise
* will pass as argument to the macro some tokens that
* are all TCommentCpp
*)
[Parenthised (xxs, info_parens)] +>
iter_token_paren (Hack.set_as_comment Token_cpp.CppMacroExpanded);
Hack.set_as_comment Token_cpp.CppMacroExpanded id;
);
apply_macro_defs xs
| PToken ({t=TIdent (s,_i1);_} as id)::xs
when Hashtbl.mem defs s ->
Hack.pr2_pp ("MACRO: found known macro = " ^ s);
(match Hashtbl.find defs s with
| Right _params, _bodymacro ->
pr2 ("macro with params but no parens found, wierd: " ^ s);
(* dont apply the macro, perhaps a redefinition *)
()
| Left (), bodymacro ->
(* special case when 1-1 substitution, we reuse the token *)
(match bodymacro with
| [newtok] ->
id.t <- (newtok +> TH.visitor_info_of_tok (fun _ ->
TH.info_of_tok id.t))
| _ ->
Hack.set_as_comment Token_cpp.CppMacroExpanded id;
id.new_tokens_before <- bodymacro;
)
);
apply_macro_defs xs
(* recurse *)
| (PToken _x)::xs -> apply_macro_defs xs
| (Parenthised (xxs, _info_parens))::xs ->
xxs +> List.iter apply_macro_defs;
apply_macro_defs xs
in
apply_macro_defs xs
(*****************************************************************************)
(* Extracting macros (from a standard.h) *)
(*****************************************************************************)
(* assumes have called fix_tokens_define before, so have TOPar_Define *)
let rec define_parse xs =
match xs with
| [] -> []
| TDefine _i1::TIdent_Define (s,_i2)::TOPar_Define _i3::xs ->
let (tokparams, _, xs) =
xs +> Common2.split_when (function TCPar _ -> true | _ -> false) in
let (body, _, xs) =
xs +> Common2.split_when
(function TCommentNewline_DefineEndOfMacro _ -> true | _ -> false) in
let params =
tokparams +> Common.map_filter (function
| TComma _ -> None
| TIdent (s, _) -> Some s
| x -> Common2.error_cant_have x
) in
let body = body +> List.map
(TH.visitor_info_of_tok Ast.make_expanded) in
let def = (s, (Right params, body)) in
def::define_parse xs
| TDefine _i1::TIdent_Define (s,_i2)::xs ->
let (body, _, xs) =
xs +> Common2.split_when
(function TCommentNewline_DefineEndOfMacro _ -> true | _ -> false) in
let body = body +> List.map
(TH.visitor_info_of_tok Ast.make_expanded) in
let def = (s, (Left (), body)) in
def::define_parse xs
| TDefine _i1::_ ->
raise Impossible
| _x::xs -> define_parse xs
let extract_macros xs =
let cleaner = xs +> List.filter (fun x -> not (TH.is_comment x)) in
define_parse cleaner