(* Yoann Padioleau * * Copyright (C) 2010-2014 Facebook * Copyright (C) 2008-2009 University of Urbana Champaign * Copyright (C) 2006-2007 Ecole des Mines de Nantes * Copyright (C) 2002 Yoann Padioleau * * 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. *) (*****************************************************************************) (* Prelude *) (*****************************************************************************) (* * This is a big file ... C++ is a big and complicated language ... * This file started with a simple AST for C. It was then extended * to deal with cpp idioms (see 'cppext:' tag), gcc extensions (see gccext), * and finally C++ constructs (see c++ext). A few kencc extensions * were also recently added (see kenccext). * * gcc introduced StatementExpr which made expr and statement mutually * recursive. It also added NestedFunc for even more mutual recursivity ... * With C++ templates, because template arguments can be types or expressions * and because templates are also qualifiers, almost all types * are now mutually recursive ... * * Like most other ASTs in flitter, it's actually more a Concrete Syntax Tree. * Some stuff are tagged 'semantic:' which means that they are computed * after parsing. * * See also lang_c/parsing/ast_c.ml and lang_clang/parsing/ast_clang.ml * (as well as mini/ast_minic.ml). * * todo: * - migrate everything to wrap2, e.g. no more expressionbis, statementbis * - support C++0x11, e.g. lambdas * * related work: * - https://github.com/facebook/facebook-clang-plugins * or https://github.com/Antique-team/clangml * but by both using clang they work after preprocessing. This is * fine for bug finding, but for codemap we need to parse as is, * and we need to do it fast (calling clang is super expensive because * calling cpp and parsing the end result is expensive) * - EDG * - see the CC'09 paper *) (*****************************************************************************) (* The AST C++ related types *) (*****************************************************************************) (* ------------------------------------------------------------------------- *) (* Token/info *) (* ------------------------------------------------------------------------- *) type tok = Parse_info.info (* a shortcut to annotate some information with token/position information *) and 'a wrap = 'a * tok list (* TODO: change to 'a * tok *) and 'a wrap2 = 'a * tok and 'a paren = tok * 'a * tok and 'a brace = tok * 'a * tok and 'a bracket = tok * 'a * tok and 'a angle = tok * 'a * tok and 'a comma_list = 'a wrap list and 'a comma_list2 = ('a, tok (* the comma *)) Common.either list (* with tarzan *) (* ------------------------------------------------------------------------- *) (* Ident, name, scope qualifier *) (* ------------------------------------------------------------------------- *) (* c++ext: in C 'name' and 'ident' are equivalent and are just strings. * In C++ 'name' can have a complex form like 'A::B::list::size'. * I use Q for qualified. I also have a special type to make the difference * between intermediate idents (the classname or template_id) and final idents. * Note that sometimes final idents are also classnames and can have final * template_id. * * Sometimes some elements are not allowed at certain places, for instance * converters can not have an associated Qtop. But I prefered to simplify * and have a unique type for all those different kinds of names. *) type name = tok (*::*) option * (qualifier * tok (*::*)) list * ident and ident = (* function name, macro name, variable, classname, enumname, namespace *) | IdIdent of simple_ident (* c++ext: *) | IdTemplateId of simple_ident * template_arguments | IdDestructor of tok(*~*) * simple_ident | IdOperator of tok * (operator * tok list) | IdConverter of tok * fullType and simple_ident = string wrap2 and template_arguments = template_argument comma_list angle and template_argument = (fullType, expression) Common.either and qualifier = | QClassname of simple_ident (* classname or namespacename *) | QTemplateId of simple_ident * template_arguments (* special cases *) and class_name = name (* only IdIdent or IdTemplateId *) and namespace_name = name (* only IdIdent *) and typedef_name = name (* only IdIdent *) and enum_name = name (* only IdIdent *) and ident_name = name (* only IdIdent *) (* TODO: do like in parsing_c/ * and ident_string = * | RegularName of string wrap * * (* cppext: *) * | CppConcatenatedName of (string wrap) wrap2 (* the ## separators *) list * (* normally only used inside list of things, as in parameters or arguments * * in which case, cf cpp-manual, it has a special meaning *) * | CppVariadicName of string wrap (* ## s *) * | CppIdentBuilder of string wrap (* s ( ) *) * * ((string wrap) wrap2 list) (* arguments *) *) (* ------------------------------------------------------------------------- *) (* Types *) (* ------------------------------------------------------------------------- *) (* We could have a more precise type in fullType, in expression, etc, but * it would require too much things at parsing time such as checking whether * there is no conflicts structname, computing value, etc. It's better to * separate concerns, so I put '=>' to mean what we would really like. In fact * what we really like is defining another fullType, expression, etc * from scratch, because many stuff are just sugar. * * invariant: Array and FunctionType have also typeQualifier but they * dont have sense. I put this to factorise some code. If you look in * grammar, you see that we can never specify const for the array * himself (but we can do it for pointer). *) and fullType = typeQualifier * typeC and typeC = typeCbis wrap (* less: rename to TBase, TPointer, etc *) and typeCbis = | BaseType of baseType | Pointer of (* '*' *) fullType (* c++ext: *) | Reference of (* '&' *) fullType | Array of constExpression option bracket * fullType | FunctionType of functionType | EnumName of tok (* 'enum' *) * simple_ident (*enum_name*) | StructUnionName of structUnion wrap2 * simple_ident (*ident_name*) (* c++ext: TypeName can now correspond also to a classname or enumname * and is a name so can have some IdTemplateId in it. *) | TypeName of name(*typedef_name*) (* only to disambiguate I think *) | TypenameKwd of tok (* 'typename' *) * name(*typedef_name*) (* gccext: TypeOfType may seems useless, why declare a __typeof__(int) * x; ? But when used with macro, it allows to fix a problem of C which * is that type declaration can be spread around the ident. Indeed it * may be difficult to have a macro such as '#define macro(type, * ident) type ident;' because when you want to do a macro(char[256], * x), then it will generate invalid code, but with a '#define * macro(type, ident) __typeof(type) ident;' it will work. *) | TypeOf of tok * (fullType, expression) Common.either paren (* should be really just at toplevel *) | EnumDef of enum_definition (* => string * int list *) (* c++ext: bigger type now *) | StructDef of class_definition (* forunparser: *) | ParenType of fullType paren and baseType = | Void | IntType of intType | FloatType of floatType (* stdC: type section. 'char' and 'signed char' are different *) and intType = | CChar (* obsolete? | CWchar *) | Si of signed (* c++ext: maybe could be put in baseType instead ? *) | CBool | WChar_t and signed = sign * base and base = | CChar2 | CShort | CInt | CLong (* gccext: *) | CLongLong and sign = Signed | UnSigned and floatType = CFloat | CDouble | CLongDouble and typeQualifier = { const: tok option; volatile: tok option; } (* TODO: like in parsing_c/ * (* gccext: cppext: *) * and attribute = attributebis wrap * and attributebis = * | Attribute of string *) (* ------------------------------------------------------------------------- *) (* Expressions *) (* ------------------------------------------------------------------------- *) (* Because of StatementExpr, we can have more 'new scope', but it's * rare I think. For instance with 'array of constExpression' we could * have an StatementExpr and a new (local) struct defined. Same for * Constructor. *) and expression = expressionbis wrap and expressionbis = (* Id can be an enumeration constant, variable, function name. * cppext: Id can also be the name of a macro. sparse says * "an identifier with a meaning is a symbol". * c++ext: Id is now a 'name' instead of a 'string' and can be * also an operator name. *) | Id of name * ident_info (* semantic: see check_variables_cpp.ml *) | C of constant (* I used to have FunCallSimple but not that useful, and we want scope info * for FunCallSimple too because can have fn(...) where fn is actually * a local *) | Call of expression * argument comma_list paren (* gccext: x ? /* empty */ : y <=> x ? x : y; *) | CondExpr of expression * expression option * expression (* should be considered as statements, bad C langage *) | Sequence of expression * expression | Assignment of expression * assignOp * expression | Postfix of expression * fixOp | Infix of expression * fixOp (* contains GetRef and Deref!! todo: lift up? *) | Unary of expression * unaryOp | Binary of expression * binaryOp * expression | ArrayAccess of expression * expression bracket (* The Pt is redundant normally, could be replace by DeRef RecordAccess *) | RecordAccess of expression * name | RecordPtAccess of expression * name (* c++ext: note that second paramater is an expression, not a name *) | RecordStarAccess of expression * expression | RecordPtStarAccess of expression * expression | SizeOfExpr of tok * expression | SizeOfType of tok * fullType paren | Cast of fullType paren * expression (* gccext: *) | StatementExpr of compound paren (* ( { } ) new scope*) (* gccext: kenccext: *) | GccConstructor of fullType paren * initialiser comma_list brace (* c++ext: *) | This of tok | ConstructedObject of fullType * argument comma_list paren | TypeId of tok * (fullType, expression) Common.either paren | CplusplusCast of cast_operator wrap2 * fullType angle * expression paren | New of tok (*::*) option * tok * argument comma_list paren option (* placement *) * fullType * argument comma_list paren option (* initializer *) | Delete of tok (*::*) option * expression | DeleteArray of tok (*::*) option * expression | Throw of expression option (* forunparser: *) | ParenExpr of expression paren | ExprTodo (* see check_variables_cpp.ml *) and ident_info = { mutable i_scope: Scope_code.scope; } (* cppext: normmally just expression *) and argument = (expression, weird_argument) Common.either and weird_argument = | ArgType of fullType (* for really unparsable stuff ... we just bailout *) | ArgAction of action_macro and action_macro = | ActMisc of tok list (* I put 'string' for Int and Float because 'int' would not be enough. * Indeed OCaml int are 31 bits. So it's simpler to use 'string'. * Same reason to have 'string' instead of 'int list' for the String case. * * note: '-2' is not a constant; it is the unary operator '-' * applied to the constant '2'. So the string must represent a positive * integer only. *) and constant = | Int of (string (* * intType*)) | Float of (string * floatType) | Char of (string * isWchar) (* normally it is equivalent to Int *) | String of (string * isWchar) | MultiString (* can contain MacroString *) (* c++ext: *) | Bool of bool and isWchar = IsWchar | IsChar and unaryOp = (* less: could be lift up, those are really important operators *) | GetRef | DeRef (* gccext: via &&label notation *) | GetRefLabel | UnPlus | UnMinus | Tilde | Not and assignOp = SimpleAssign | OpAssign of arithOp and fixOp = Dec | Inc and binaryOp = Arith of arithOp | Logical of logicalOp and arithOp = | Plus | Minus | Mul | Div | Mod | DecLeft | DecRight | And | Or | Xor and logicalOp = | Inf | Sup | InfEq | SupEq | Eq | NotEq | AndLog | OrLog (* c++ext: used elsewhere but prefer to define it close to other operators *) and ptrOp = PtrStarOp | PtrOp and allocOp = NewOp | DeleteOp | NewArrayOp | DeleteArrayOp and accessop = ParenOp | ArrayOp and operator = | BinaryOp of binaryOp | AssignOp of assignOp | FixOp of fixOp | PtrOpOp of ptrOp | AccessOp of accessop | AllocOp of allocOp | UnaryTildeOp | UnaryNotOp | CommaOp (* c++ext: *) and cast_operator = | Static_cast | Dynamic_cast | Const_cast | Reinterpret_cast and constExpression = expression (* => int *) (* ------------------------------------------------------------------------- *) (* Statements *) (* ------------------------------------------------------------------------- *) (* note: assignement is not a statement, it's an expression :( * (wonderful C language). * note: I use 'and' for type definition because gccext allows statements as * expressions, so we need mutual recursive type definition now. *) and statement = statementbis wrap and statementbis = | Compound of compound (* new scope *) | ExprStatement of exprStatement | Labeled of labeled | Selection of selection | Iteration of iteration | Jump of jump (* c++ext: in C this constructor could be outside the statement type, in a * decl type, because declarations are only at the beginning of a compound * normally. But in C++ we can freely mix declarations and statements. *) | DeclStmt of block_declaration (* c++ext: *) | Try of tok * compound * handler list (* gccext: *) | NestedFunc of func_definition (* cppext: *) | MacroStmt | StmtTodo (* cppext: c++ext: * old: compound = (declaration list * statement list) * old: (declaration, statement) either list *) and compound = statement_sequencable list brace and exprStatement = expression option and labeled = | Label of string * statement | Case of expression * statement | CaseRange of expression * expression * statement (* gccext: *) | Default of statement and selection = | If of tok * expression paren * statement * tok option * statement (* need to check that all elements in the compound start * with a case:, otherwise it's unreachable code. *) | Switch of tok * expression paren * statement and iteration = | While of tok * expression paren * statement | DoWhile of tok * statement * tok * expression paren * tok (*;*) | For of tok * (exprStatement wrap * exprStatement wrap * exprStatement wrap) paren * statement (* cppext: *) | MacroIteration of simple_ident * argument comma_list paren * statement and jump = | Goto of string | Continue | Break | Return | ReturnExpr of expression (* gccext: goto *exp ';' *) | GotoComputed of expression (* c++ext: *) and handler = tok * exception_declaration paren * compound and exception_declaration = | ExnDeclEllipsis of tok | ExnDecl of parameter (* easier to put at statement_list level than statement level *) and statement_sequencable = | StmtElem of statement (* cppext: *) | CppDirectiveStmt of cpp_directive | IfdefStmt of ifdef_directive (* * statement list *) (* ------------------------------------------------------------------------- *) (* Block Declaration *) (* ------------------------------------------------------------------------- *) (* a.k.a declaration_statement *) and block_declaration = (* Before I had a Typedef constructor, but why make this special case and not * have also StructDef, EnumDef, so that 'struct t {...} v' which would * then generate two declarations. If you want a cleaner C AST use * ast_c.ml. * note: before the need for unparser, I didn't have a DeclList but just * a Decl. *) | DeclList of onedecl comma_list * tok (*;*) (* cppext: todo? now factorize with MacroTop ? *) | MacroDecl of tok list * simple_ident * argument comma_list paren * tok (* c++ext: using namespace *) | UsingDecl of (tok * name * tok (*;*)) | UsingDirective of tok * tok (*'namespace'*) * namespace_name * tok(*;*) | NameSpaceAlias of tok * simple_ident * tok (*=*) * namespace_name * tok(*;*) (* gccext: *) | Asm of tok * tok option (*volatile*) * asmbody paren * tok(*;*) (* gccext: *) and asmbody = tok list (* string list *) * colon wrap (* : *) list and colon = Colon of colon_option comma_list and colon_option = colon_optionbis wrap and colon_optionbis = ColonMisc | ColonExpr of expression paren (* ------------------------------------------------------------------------- *) (* Variable definition (and also field definition) *) (* ------------------------------------------------------------------------- *) (* note: onedecl includes prototype declarations and class_declarations! * c++ext: onedecl now covers also field definitions as fields can have * storage in C++. *) and onedecl = { (* option cos can have empty declaration or struct tag declaration. * kenccext: name can also be empty because of anonymous fields. *) v_namei: (name * init option) option; v_type: fullType; v_storage: storage; (* v_attr: attribute list; *) (* gccext: *) } and storage = NoSto | StoTypedef of tok | Sto of storageClass wrap2 and storageClass = Auto | Static | Register | Extern (* Friend ???? Mutable? *) (*c++ext: TODO *) (* I am not sure what it means to declare a prototype inline, but gcc * accepts it. *) and _func_specifier = Inline | Virtual and init = | EqInit of tok (*=*) * initialiser (* c++ext: constructed object *) | ObjInit of argument comma_list paren and initialiser = | InitExpr of expression | InitList of initialiser comma_list brace (* gccext: *) | InitDesignators of designator list * tok (*=*) * initialiser | InitFieldOld of simple_ident * tok (*:*) * initialiser | InitIndexOld of expression bracket * initialiser (* ex: [2].y = x, or .y[2] or .y.x. They can be nested *) and designator = | DesignatorField of tok(*:*) * simple_ident | DesignatorIndex of expression bracket | DesignatorRange of (expression * tok (*...*) * expression) bracket (* ------------------------------------------------------------------------- *) (* Function definition *) (* ------------------------------------------------------------------------- *) (* Normally we should define another type functionType2 because there * are more restrictions on what can define a function than a pointer * function. For instance a function declaration can omit the name of the * parameter wheras a function definition can not. But, in some cases such * as 'f(void) {', there is no name too, so I simplified and reused the * same functionType type for both declarations and function definitions. *) and func_definition = { f_name: name; f_type: functionType; f_storage: storage; (* todo: gccext: inline or not:, f_inline: tok option *) f_body: compound; (*f_attr: attribute list;*) (* gccext: *) } and functionType = { ft_ret: fullType; (* fake return type for ctor/dtor *) ft_params: parameter comma_list paren; ft_dots: (tok(*,*) * tok(*...*)) option; (* c++ext: *) ft_const: tok option; (* only for methods *) ft_throw: exn_spec option; } and parameter = { p_name: simple_ident option; p_type: fullType; p_register: tok option; (* c++ext: *) p_val: (tok (*=*) * expression) option; } and exn_spec = (tok * name comma_list2 paren) (* less: simplify? need differentiate at this level? could have * is_ctor, is_dtor helper instead. *) and func_or_else = | FunctionOrMethod of func_definition (* c++ext: special member function *) | Constructor of func_definition (* TODO explicit/inline, chain_call *) | Destructor of func_definition and method_decl = | MethodDecl of onedecl * (tok * tok) option (* '=' '0' *) * tok(*;*) | ConstructorDecl of simple_ident * parameter comma_list paren * tok(*;*) | DestructorDecl of tok(*~*) * simple_ident * tok option paren * exn_spec option * tok(*;*) (* ------------------------------------------------------------------------- *) (* enum definition *) (* ------------------------------------------------------------------------- *) (* less: use a record *) and enum_definition = tok (*enum*) * simple_ident option * enum_elem comma_list brace and enum_elem = { e_name: simple_ident; e_val: (tok (*=*) * constExpression) option; } (* ------------------------------------------------------------------------- *) (* Class definition *) (* ------------------------------------------------------------------------- *) and class_definition = { c_kind: structUnion wrap2; (* the ident can be a template_id when do template specialization. *) c_name: ident_name(*class_name??*) option; (* c++ext: *) c_inherit: (tok (* ':' *) * base_clause comma_list) option; c_members: class_member_sequencable list brace (* new scope *); } and structUnion = | Struct | Union (* c++ext: *) | Class and base_clause = { i_name: class_name; i_virtual: tok option; i_access: access_spec wrap2 option; } (* used in inheritance spec (base_clause) and class_member *) and access_spec = Public | Private | Protected (* was called field wrap before *) and class_member = (* could put outside and take class_member list *) | Access of access_spec wrap2 * tok (*:*) (* before unparser, I didn't have a FieldDeclList but just a Field. *) | MemberField of fieldkind comma_list * tok (*';'*) | MemberFunc of func_or_else | MemberDecl of method_decl | QualifiedIdInClass of name (* ?? *) * tok(*;*) | TemplateDeclInClass of (tok * template_parameters * declaration) | UsingDeclInClass of (tok (*using*) * name * tok (*;*)) (* gccext: and maybe c++ext: *) | EmptyField of tok (*;*) (* At first I thought that a bitfield could be only Signed/Unsigned. * But it seems that gcc allow char i:4. C rule must say that you * can cast into int so enum too, ... * c++ext: FieldDecl was before Simple of string option * fullType * but in c++ fields can also have storage (e.g. static) so now reuse * ondecl. *) and fieldkind = | FieldDecl of onedecl | BitField of simple_ident option * tok(*:*) * fullType * constExpression (* fullType => BitFieldInt | BitFieldUnsigned *) and class_member_sequencable = | ClassElem of class_member (* cppext: *) | CppDirectiveStruct of cpp_directive | IfdefStruct of ifdef_directive (* * field list *) (* ------------------------------------------------------------------------- *) (* cppext: cpp directives, #ifdef, #define and #include body *) (* ------------------------------------------------------------------------- *) and cpp_directive = | Define of tok (* #define*) * simple_ident * define_kind * define_val | Include of tok (* #include s *) * inc_kind * string (* path *) | Undef of simple_ident (* #undef xxx *) | PragmaAndCo of tok and define_kind = | DefineVar | DefineFunc of string wrap comma_list paren and define_val = | DefineExpr of expression | DefineStmt of statement | DefineType of fullType | DefineFunction of func_definition | DefineInit of initialiser (* in practice only { } with possible ',' *) (* ?? *) | DefineText of string wrap | DefineEmpty | DefineDoWhileZero of statement wrap (* do { } while(0) *) | DefinePrintWrapper of tok (* if *) * expression paren * name | DefineTodo and inc_kind = | Local (* "" *) | Standard (* <> *) | Weird (* ex: #include SYSTEM_H *) (* less: 'a ifdefed = 'a list wrap (* ifdef elsif else endif *) *) and ifdef_directive = ifdefkind wrap2 and ifdefkind = | Ifdef (* todo? of string? *) (* less: IfIf of formula_cpp ? *) | IfdefElse | IfdefElseif | IfdefEndif (* less: * set in Parsing_hacks.set_ifdef_parenthize_info. It internally use * a global so it means if you parse the same file twice you may get * different id. I try now to avoid this pb by resetting it each * time I parse a file. * * and matching_tag = * IfdefTag of (int (* tag *) * int (* total with this tag *)) *) (* ------------------------------------------------------------------------- *) (* The toplevel elements *) (* ------------------------------------------------------------------------- *) (* it's not really 'toplevel' because the elements below can be nested * inside namespaces or some extern. It's not really 'declaration' * either because it can defines stuff. But I keep the C++ standard * terminology. * * note that we use 'block_declaration' below, not 'statement'. *) and declaration = | BlockDecl of block_declaration (* include struct/globals/... definitions *) | Func of func_or_else (* c++ext: *) | TemplateDecl of tok * template_parameters * declaration | TemplateSpecialization of tok * unit angle * declaration (* the list can be empty *) | ExternC of tok * tok * declaration | ExternCList of tok * tok * declaration_sequencable list brace (* the list can be empty *) | NameSpace of tok * simple_ident * declaration_sequencable list brace (* after have some semantic info *) | NameSpaceExtend of string * declaration_sequencable list | NameSpaceAnon of tok * declaration_sequencable list brace (* gccext: allow redundant ';' *) | EmptyDef of tok | DeclTodo (* c++ext: *) and template_parameter = parameter (* todo? more? *) and template_parameters = template_parameter comma_list angle (* easier to put at statement_list level than statement level *) and declaration_sequencable = | DeclElem of declaration (* cppext: *) | CppDirectiveDecl of cpp_directive | IfdefDecl of ifdef_directive (* * toplevel list *) (* cppext: *) | MacroTop of simple_ident * argument comma_list paren * tok option | MacroVarTop of simple_ident * tok (* ; *) (* could also be in decl *) | NotParsedCorrectly of tok list and toplevel = declaration_sequencable and program = toplevel list (* ------------------------------------------------------------------------- *) (* Any *) (* ------------------------------------------------------------------------- *) and any = | Program of program | Toplevel of toplevel | Cpp of cpp_directive | Stmt of statement | Expr of expression | Type of fullType | Name of name | BlockDecl2 of block_declaration | ClassDef of class_definition | FuncDef of func_definition | FuncOrElse of func_or_else | ClassMember of class_member | OneDecl of onedecl | Init of initialiser | Constant of constant | Argument of argument | Parameter of parameter | Body of compound | Info of tok | InfoList of tok list (* with tarzan *) (*****************************************************************************) (* Some constructors *) (*****************************************************************************) let nQ = {const=None; volatile= None} let noIdInfo () = { i_scope = Scope_code.NoScope; } let noii = [] let noQscope = [] (*****************************************************************************) (* Wrappers *) (*****************************************************************************) let unwrap x = fst x let uncomma xs = List.map fst xs let unparen (_, x, _) = x let unbrace (_, x, _) = x let unwrap_typeC (_qu, (typeC, _ii)) = typeC (* When want add some info in AST that does not correspond to * an existing C element. * old: when don't want 'synchronize' on it in unparse_c.ml * (now have other mark for tha matter). * used by parsing hacks *) let make_expanded ii = let noVirtPos = ({Parse_info.str="";charpos=0;line=0;column=0;file=""},-1) in let (a, b) = noVirtPos in { ii with Parse_info.token = Parse_info.ExpandedTok (Parse_info.get_original_token_location ii.Parse_info.token, a, b) } (* used by parsing hacks *) let rewrap_pinfo pi ii = {ii with Parse_info.token = pi} (* used while migrating the use of 'string' to 'name' in check_variables *) let (string_of_name_tmp: name -> string) = fun name -> let (_opt, _qu, id) = name in match id with | IdIdent (s,_) -> s | _ -> failwith "TODO:string_of_name_tmp" let (ii_of_id_name: name -> tok list) = fun name -> let (_opt, _qu, id) = name in match id with | IdIdent (_s,ii) -> [ii] | IdOperator (_, (_op, ii)) -> ii | IdConverter (_tok, _ft) -> failwith "ii_of_id_name: IdConverter" | IdDestructor (tok, (_s, ii)) -> [tok;ii] | IdTemplateId ((_s, ii), _args) -> [ii]