/*
-* Copyright 2005-2008, Ecole des Mines de Nantes, University of Copenhagen
-* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
-* This file is part of Coccinelle.
-*
-* Coccinelle is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, according to version 2 of the License.
-*
-* Coccinelle 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
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
-*
-* The authors reserve the right to distribute this or future versions of
-* Coccinelle under other licenses.
-*/
+ * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
+ * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
+ * This file is part of Coccinelle.
+ *
+ * Coccinelle is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, according to version 2 of the License.
+ *
+ * Coccinelle 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The authors reserve the right to distribute this or future versions of
+ * Coccinelle under other licenses.
+ */
%{
%token EOF
%token TIdentifier TExpression TStatement TFunction TLocal TType TParameter
-%token TIdExpression
+%token TIdExpression TInitialiser
%token Tlist TFresh TConstant TError TWords TWhy0 TPlus0 TBang0
-%token TPure TContext
+%token TPure TContext TGenerated
%token TTypedef TDeclarer TIterator TName TPosition TPosAny
-%token TUsing TDisable TExtends TDepends TOn TEver TNever TExists TForall TScript
-%token TReverse TNothing
+%token TUsing TDisable TExtends TDepends TOn TEver TNever TExists TForall
+%token TScript TInitialize TFinalize TNothing TVirtual
%token<string> TRuleName
%token<Data.clt> Tchar Tshort Tint Tdouble Tfloat Tlong
-%token<Data.clt> Tvoid Tstruct Tunion
+%token<Data.clt> Tvoid Tstruct Tunion Tenum
%token<Data.clt> Tunsigned Tsigned
%token<Data.clt> Tstatic Tauto Tregister Textern Tinline Ttypedef
%token <Data.clt> TIf TElse TWhile TFor TDo TSwitch TCase TDefault TReturn
%token <Data.clt> TBreak TContinue TGoto TSizeof TFunDecl
-%token <string * Data.clt> TIdent TTypeId TDeclarerId TIteratorId
+%token <string * Data.clt> TIdent TTypeId TDeclarerId TIteratorId TPragma
-%token <Parse_aux.idinfo> TMetaId TMetaFunc TMetaLocalFunc
-%token <Parse_aux.idinfo> TMetaIterator TMetaDeclarer
-%token <Parse_aux.expinfo> TMetaErr
-%token <Parse_aux.info> TMetaParam TMetaStm TMetaStmList TMetaType
-%token <Parse_aux.list_info> TMetaParamList TMetaExpList
-%token <Parse_aux.typed_info> TMetaExp TMetaIdExp TMetaLocalIdExp TMetaConst
-%token <Parse_aux.pos_info> TMetaPos
+%token <Parse_aux.idinfo> TMetaId TMetaFunc TMetaLocalFunc
+%token <Parse_aux.idinfo> TMetaIterator TMetaDeclarer
+%token <Parse_aux.expinfo> TMetaErr
+%token <Parse_aux.info> TMetaParam TMetaStm TMetaStmList TMetaType
+%token <Parse_aux.info> TMetaInit
+%token <Parse_aux.list_info> TMetaParamList TMetaExpList
+%token <Parse_aux.typed_expinfo> TMetaExp TMetaIdExp TMetaLocalIdExp TMetaConst
+%token <Parse_aux.pos_info> TMetaPos
%token TArob TArobArob TPArob
%token <string> TScriptData
%token <Data.clt> TWhy TDotDot TBang TOPar TOPar0
%token <Data.clt> TMid0 TCPar TCPar0
-%token <string> TPragma TPathIsoFile
+%token <string> TPathIsoFile
%token <string * Data.clt> TIncludeL TIncludeNL
%token <Data.clt * token> TDefine
-%token <Data.clt * token * int> TDefineParam
+%token <Data.clt * token * int * int> TDefineParam
%token <string * Data.clt> TMinusFile TPlusFile
%token <Data.clt> TInc TDec
%token <Data.clt> TAndLog
%token <Data.clt> TOr
%token <Data.clt> TXor
-%token <Data.clt> TAnd
-%token <Data.clt> TEqEq TNotEq
+%token <Data.clt> TAnd
+%token <Data.clt> TEqEq TNotEq TTildeEq TTildeExclEq
%token <Ast_cocci.logicalOp * Data.clt> TLogOp /* TInf TSup TInfEq TSupEq */
%token <Ast_cocci.arithOp * Data.clt> TShOp /* TShl TShr */
%token <Ast_cocci.arithOp * Data.clt> TDmOp /* TDiv TMod */
%token <Data.clt> TPtrOp
-%token TMPtVirg
+%token TMPtVirg TCppConcatOp
%token <Data.clt> TEq TDot TComma TPtVirg
%token <Ast_cocci.assignOp * Data.clt> TAssign
%left TAndLog
%left TOr
%left TXor
-%left TAnd
+%left TAnd
%left TEqEq TNotEq
%left TLogOp /* TInf TSup TInfEq TSupEq */
%left TShOp /* TShl TShr */
%type <Ast0_cocci.rule> plus_exp_main
%start include_main
-%type <(string,string) Common.either list> include_main
+%type <Data.incl_iso list> include_main
%start iso_rule_name
%type <Ast_cocci.rulename>
%start meta_main
%type <(Ast_cocci.metavar,Ast_cocci.metavar) Common.either list> meta_main
-%start <string * (string * string)> script_meta_main
+%start <string * Ast_cocci.meta_name> script_meta_main
%start iso_main
%type <Ast0_cocci.anything list list> iso_main
nm=ioption(pure_ident) extends d=depends i=loption(choose_iso)
a=loption(disable) e=exists ee=is_expression TArob
{ P.make_cocci_rule_name_result nm d i a e ee }
+ | TGenerated extends d=depends i=loption(choose_iso)
+ a=loption(disable) e=exists ee=is_expression TArob
+ /* these rules have no name as a cheap way to ensure that no normal
+ rule inherits their metavariables or depends on them */
+ { P.make_generated_rule_name_result None d i a e ee }
| TScript TDotDot lang=pure_ident d=depends TArob
{ P.make_script_rule_name_result lang d }
+ | TInitialize TDotDot lang=pure_ident TArob
+ { P.make_initial_script_rule_name_result lang }
+ | TFinalize TDotDot lang=pure_ident TArob
+ { P.make_final_script_rule_name_result lang }
extends:
/* empty */ { () }
exists:
TExists { Ast.Exists }
| TForall { Ast.Forall }
-| TReverse TForall { Ast.ReverseForall }
| { Ast.Undetermined }
is_expression: // for more flexible parsing of top level expressions
| list(incl) TArobArob { $1 }
incl:
- TUsing TString { Common.Left(P.id2name $2) }
-| TUsing TPathIsoFile { Common.Right $2 }
+ TIncludeL { let (x,_) = $1 in Data.Include(x) }
+| TUsing TString { Data.Iso(Common.Left(P.id2name $2)) }
+| TUsing TPathIsoFile { Data.Iso(Common.Right $2) }
+| TVirtual comma_list(pure_ident)
+ { let names = List.map P.id2name $2 in
+ (* ensure that the names of virtual and real rules don't overlap *)
+ List.iter
+ (function name -> Hashtbl.add Data.all_metadecls name [])
+ names;
+ Data.Virt(names) }
metadec:
ar=arity ispure=pure
kindfn=metakind ids=comma_list(pure_ident_or_meta_ident) TMPtVirg
{ P.create_metadec ar ispure kindfn ids }
+| kindfn=metakind_fresh ids=comma_list(pure_ident_or_meta_ident_with_seed)
+ TMPtVirg
+ { P.create_fresh_metadec kindfn ids }
+| ar=arity ispure=pure
+ kindfn=metakind_atomic_maybe_virt
+ ids=
+ comma_list(pure_ident_or_meta_ident_with_idconstraint_virt(re_or_not_eqid))
+ TMPtVirg
+ { let (normal,virt) = Common.partition_either (fun x -> x) ids in
+ let (idfn,virtfn) = kindfn in
+ function cr ->
+ (P.create_metadec_with_constraints ar ispure idfn normal cr) @
+ (P.create_metadec_virt ar ispure virtfn virt cr) }
| ar=arity ispure=pure
kindfn=metakind_atomic
- ids=comma_list(pure_ident_or_meta_ident_with_not_eq(not_eq)) TMPtVirg
- { P.create_metadec_ne ar ispure kindfn ids }
+ ids=comma_list(pure_ident_or_meta_ident_with_idconstraint(re_or_not_eqid))
+ TMPtVirg
+ { P.create_metadec_with_constraints ar ispure kindfn ids }
| ar=arity ispure=pure
kindfn=metakind_atomic_expi
- ids=comma_list(pure_ident_or_meta_ident_with_not_eq(not_eqe)) TMPtVirg
- { P.create_metadec_ne ar ispure kindfn ids }
+ ids=comma_list(pure_ident_or_meta_ident_with_econstraint(re_or_not_eqe))
+ TMPtVirg
+ { P.create_metadec_with_constraints ar ispure kindfn ids }
| ar=arity ispure=pure
kindfn=metakind_atomic_expe
- ids=comma_list(pure_ident_or_meta_ident_with_not_eq(not_ceq)) TMPtVirg
- { P.create_metadec_ne ar ispure kindfn ids }
+ ids=comma_list(pure_ident_or_meta_ident_with_x_eq(not_ceq)) TMPtVirg
+ { P.create_metadec_with_constraints ar ispure kindfn ids }
| ar=arity TPosition a=option(TPosAny)
- ids=comma_list(pure_ident_or_meta_ident_with_not_eq(not_pos)) TMPtVirg
- { let kindfn arity name pure check_meta constraints =
+ ids=comma_list(pure_ident_or_meta_ident_with_x_eq(not_pos)) TMPtVirg
+ (* pb: position variables can't be inherited from normal rules, and then
+ there is no way to inherit from a generated rule, so there is no point
+ to have a position variable *)
+ { (if !Data.in_generating
+ then failwith "position variables not allowed in a generated rule file");
+ let kindfn arity name pure check_meta constraints =
let tok = check_meta(Ast.MetaPosDecl(arity,name)) in
let any = match a with None -> Ast.PER | Some _ -> Ast.ALL in
!Data.add_pos_meta name constraints any; tok in
- P.create_metadec_ne ar false kindfn ids }
+ P.create_metadec_with_constraints ar false kindfn ids }
| ar=arity ispure=pure
TParameter Tlist TOCro id=pure_ident_or_meta_ident TCCro
ids=comma_list(pure_ident_or_meta_ident) TMPtVirg
!Data.add_explist_meta name (Some lenname) pure; tok)
id ids }
-%inline metakind:
+%inline metakind_fresh:
TFresh TIdentifier
- { (fun arity name pure check_meta ->
- let tok = check_meta(Ast.MetaFreshIdDecl(arity,name)) in
- !Data.add_id_meta name [] pure; tok) }
-| TParameter
+ { (fun name check_meta seed ->
+ let tok = check_meta(Ast.MetaFreshIdDecl(name,seed)) in
+ !Data.add_fresh_id_meta name; tok) }
+
+/* metavariable kinds with no constraints, etc */
+%inline metakind:
+ TParameter
{ (fun arity name pure check_meta ->
let tok = check_meta(Ast.MetaParamDecl(arity,name)) in
!Data.add_param_meta name pure; tok) }
| TType
{ (fun arity name pure check_meta ->
let tok = check_meta(Ast.MetaTypeDecl(arity,name)) in
- !Data.add_type_meta name pure; tok) }
+ !Data.add_type_meta name pure; tok) }
+| TInitialiser
+ { (fun arity name pure check_meta ->
+ let tok = check_meta(Ast.MetaInitDecl(arity,name)) in
+ !Data.add_init_meta name pure; tok) }
| TStatement
{ (fun arity name pure check_meta ->
let tok = check_meta(Ast.MetaStmDecl(arity,name)) in
then (!Data.add_iterator_name name; [])
else raise (Semantic_cocci.Semantic "bad iterator")) }
+%inline metakind_atomic_maybe_virt:
+ TIdentifier
+ {
+ let idfn arity name pure check_meta constraints =
+ let tok = check_meta(Ast.MetaIdDecl(arity,name)) in
+ !Data.add_id_meta name constraints pure; tok in
+ let virtfn arity name pure check_meta virtual_env =
+ try
+ let vl = List.assoc name virtual_env in
+ !Data.add_virt_id_meta_found name vl; []
+ with Not_found ->
+ let name = ("virtual",name) in
+ let tok = check_meta(Ast.MetaIdDecl(arity,name)) in
+ !Data.add_virt_id_meta_not_found name pure; tok in
+ (idfn,virtfn) }
%inline metakind_atomic:
- TIdentifier
- { (fun arity name pure check_meta constraints ->
- let tok = check_meta(Ast.MetaIdDecl(arity,name)) in
- !Data.add_id_meta name constraints pure; tok) }
-| TFunction
+ TFunction
{ (fun arity name pure check_meta constraints ->
let tok = check_meta(Ast.MetaFuncDecl(arity,name)) in
!Data.add_func_meta name constraints pure; tok) }
TExpression
{ (fun arity name pure check_meta constraints ->
let tok = check_meta(Ast.MetaExpDecl(arity,name,None)) in
- !Data.add_exp_meta None name constraints pure; tok) }
+ !Data.add_exp_meta None name (Ast0.NotExpCstrt constraints) pure; tok) }
| vl=meta_exp_type // no error if use $1 but doesn't type check
{ (fun arity name pure check_meta constraints ->
let ty = Some vl in
if not
(List.exists
(function
- Type_cocci.BaseType(Type_cocci.IntType,_) -> true
- | Type_cocci.BaseType(Type_cocci.ShortType,_) -> true
- | Type_cocci.BaseType(Type_cocci.LongType,_) -> true
+ Type_cocci.BaseType(Type_cocci.IntType) -> true
+ | Type_cocci.BaseType(Type_cocci.ShortType) -> true
+ | Type_cocci.BaseType(Type_cocci.LongType) -> true
| _ -> false)
vl)
then failwith "metavariable with int constraint must be an int"
| _ -> ())
constraints;
let tok = check_meta(Ast.MetaExpDecl(arity,name,ty)) in
- !Data.add_exp_meta ty name constraints pure; tok) }
+ !Data.add_exp_meta ty name (Ast0.NotExpCstrt constraints) pure; tok)
+ }
meta_exp_type:
- t=ctype
+ t=typedef_ctype
{ [Ast0_cocci.ast0_type_to_type t] }
| TOBrace t=comma_list(ctype) TCBrace m=list(TMul)
{ List.map
| TPlus0 { Ast.MULTI }
| /* empty */ { Ast.NONE }
-generic_ctype:
- q=ctype_qualif
- { Ast0.wrap(Ast0.ImplicitInt(q)) }
- | q=ioption(ctype_qualif) ty=Tchar
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.CharType ty, q)) }
- | q=ioption(ctype_qualif) ty=Tshort
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.ShortType ty, q)) }
- | q=ioption(ctype_qualif) ty=Tint
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.IntType ty, q)) }
- | t=Tdouble
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.DoubleType t, None)) }
- | t=Tfloat
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.FloatType t, None)) }
- | q=ioption(ctype_qualif) ty=Tlong
- { Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.LongType ty, q)) }
- | s=struct_or_union i=ident
- { Ast0.wrap(Ast0.StructUnionName(s, Some i)) }
- | s=struct_or_union i=ioption(ident)
- l=TOBrace d=struct_decl_list r=TCBrace
- { (if i = None && !Data.in_iso
- then failwith "structures must be named in the iso file");
- Ast0.wrap(Ast0.StructUnionDef(Ast0.wrap(Ast0.StructUnionName(s, i)),
- P.clt2mcode "{" l,
- d, P.clt2mcode "}" r)) }
- | s=TMetaType l=TOBrace d=struct_decl_list r=TCBrace
- { let (nm,pure,clt) = s in
- let ty =
- Ast0.wrap(Ast0.MetaType(P.clt2mcode nm clt,pure)) in
- Ast0.wrap
- (Ast0.StructUnionDef(ty,P.clt2mcode "{" l,d,P.clt2mcode "}" r)) }
- | r=TRuleName TDot p=TIdent
- { let nm = (r,P.id2name p) in
- (* this is only possible when we are in a metavar decl. Otherwise,
- it will be represented already as a MetaType *)
- let _ = P.check_meta(Ast.MetaTypeDecl(Ast.NONE,nm)) in
- Ast0.wrap(Ast0.MetaType(P.clt2mcode nm (P.id2clt p),
- Ast0.Impure (*will be ignored*))) }
- | p=TTypeId
- { Ast0.wrap(Ast0.TypeName(P.id2mcode p)) }
- | p=TMetaType
- { let (nm,pure,clt) = p in
- Ast0.wrap(Ast0.MetaType(P.clt2mcode nm clt,pure)) }
+/* ---------------------------------------------------------------------- */
+
+%inline
+signable_types:
+ ty=Tchar
+ { Ast0.wrap(Ast0.BaseType(Ast.CharType,[P.clt2mcode "char" ty])) }
+| ty=Tshort
+ { Ast0.wrap(Ast0.BaseType(Ast.ShortType,[P.clt2mcode "short" ty])) }
+| ty=Tint
+ { Ast0.wrap(Ast0.BaseType(Ast.IntType,[P.clt2mcode "int" ty])) }
+| p=TMetaType
+ { let (nm,pure,clt) = p in
+ Ast0.wrap(Ast0.MetaType(P.clt2mcode nm clt,pure)) }
+| r=TRuleName TDot p=TIdent
+ { let nm = (r,P.id2name p) in
+ (* this is only possible when we are in a metavar decl. Otherwise,
+ it will be represented already as a MetaType *)
+ let _ = P.check_meta(Ast.MetaTypeDecl(Ast.NONE,nm)) in
+ Ast0.wrap(Ast0.MetaType(P.clt2mcode nm (P.id2clt p),
+ Ast0.Impure (*will be ignored*))) }
+| ty=Tlong
+ { Ast0.wrap(Ast0.BaseType(Ast.LongType,[P.clt2mcode "long" ty])) }
+| ty1=Tlong ty2=Tlong
+ { Ast0.wrap
+ (Ast0.BaseType
+ (Ast.LongLongType,
+ [P.clt2mcode "long" ty1;P.clt2mcode "long" ty2])) }
+
+%inline
+non_signable_types:
+ ty=Tvoid
+ { Ast0.wrap(Ast0.BaseType(Ast.VoidType,[P.clt2mcode "void" ty])) }
+| ty=Tdouble
+ { Ast0.wrap(Ast0.BaseType(Ast.DoubleType,[P.clt2mcode "double" ty])) }
+| ty=Tfloat
+ { Ast0.wrap(Ast0.BaseType(Ast.FloatType,[P.clt2mcode "float" ty])) }
+| s=Tenum i=ident
+ { Ast0.wrap(Ast0.EnumName(P.clt2mcode "enum" s, i)) }
+| s=struct_or_union i=ident
+ { Ast0.wrap(Ast0.StructUnionName(s, Some i)) }
+| s=struct_or_union i=ioption(ident)
+ l=TOBrace d=struct_decl_list r=TCBrace
+ { (if i = None && !Data.in_iso
+ then failwith "structures must be named in the iso file");
+ Ast0.wrap(Ast0.StructUnionDef(Ast0.wrap(Ast0.StructUnionName(s, i)),
+ P.clt2mcode "{" l,
+ d, P.clt2mcode "}" r)) }
+| s=TMetaType l=TOBrace d=struct_decl_list r=TCBrace
+ { let (nm,pure,clt) = s in
+ let ty = Ast0.wrap(Ast0.MetaType(P.clt2mcode nm clt,pure)) in
+ Ast0.wrap(Ast0.StructUnionDef(ty,P.clt2mcode "{" l,d,P.clt2mcode "}" r)) }
+| p=TTypeId
+ { Ast0.wrap(Ast0.TypeName(P.id2mcode p)) }
+
+%inline
+all_basic_types:
+ r=Tsigned ty=signable_types
+ { Ast0.wrap(Ast0.Signed(P.clt2mcode Ast.Signed r,Some ty)) }
+| r=Tunsigned ty=signable_types
+ { Ast0.wrap(Ast0.Signed(P.clt2mcode Ast.Unsigned r,Some ty)) }
+| ty=signable_types { ty }
+| ty=non_signable_types { ty }
+
+ctype:
+ cv=ioption(const_vol) ty=all_basic_types m=list(TMul)
+ { P.pointerify (P.make_cv cv ty) m }
+| r=Tsigned
+ { Ast0.wrap(Ast0.Signed(P.clt2mcode Ast.Signed r,None)) }
+| r=Tunsigned
+ { Ast0.wrap(Ast0.Signed(P.clt2mcode Ast.Unsigned r,None)) }
+| lp=TOPar0 t=midzero_list(ctype,ctype) rp=TCPar0
+ { let (mids,code) = t in
+ Ast0.wrap
+ (Ast0.DisjType(P.clt2mcode "(" lp,code,mids, P.clt2mcode ")" rp)) }
+
+/* signed, unsigned alone not allowed */
+typedef_ctype:
+ cv=ioption(const_vol) ty=all_basic_types m=list(TMul)
+ { P.pointerify (P.make_cv cv ty) m }
+| lp=TOPar0 t=midzero_list(ctype,ctype) rp=TCPar0
+ { let (mids,code) = t in
+ Ast0.wrap
+ (Ast0.DisjType(P.clt2mcode "(" lp,code,mids, P.clt2mcode ")" rp)) }
+
+/* ---------------------------------------------------------------------- */
struct_or_union:
s=Tstruct { P.clt2mcode Ast.Struct s }
| t=ctype d=d_ident pv=TPtVirg
{ let (id,fn) = d in
[Ast0.wrap(Ast0.UnInit(None,fn t,id,P.clt2mcode ";" pv))] }
- | t=fn_ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
+ | t=ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
lp2=TOPar p=decl_list(name_opt_decl) rp2=TCPar pv=TPtVirg
{ let (id,fn) = d in
let t =
| struct_decl struct_decl_list_start { $1@$2 }
| struct_decl { $1 }
-ctype:
- cv=ioption(const_vol) ty=generic_ctype m=list(TMul)
- { P.pointerify (P.make_cv cv ty) m }
- | cv=ioption(const_vol) t=Tvoid m=nonempty_list(TMul)
- { let ty =
- Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.VoidType t, None)) in
- P.pointerify (P.make_cv cv ty) m }
- | lp=TOPar0 t=midzero_list(ctype,ctype) rp=TCPar0
- /* more hacks */
- { let (mids,code) = t in
- Ast0.wrap
- (Ast0.DisjType(P.clt2mcode "(" lp,code,mids, P.clt2mcode ")" rp)) }
-
-
-fn_ctype: // allows metavariables
- ty=generic_ctype m=list(TMul) { P.pointerify ty m }
- | t=Tvoid m=list(TMul)
- { P.pointerify
- (Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.VoidType t, None)))
- m }
-
-ctype_qualif:
- Tunsigned { P.clt2mcode Ast.Unsigned $1 }
- | Tsigned { P.clt2mcode Ast.Signed $1 }
/*****************************************************************************/
/* have to inline everything to avoid conflicts? switch to proper
declarations, statements, and expressions for the subterms */
-minus_body:
+minus_body:
f=loption(filespec)
b=loption(minus_start)
- ew=loption(error_words)
- { match f@b@ew with
+ /*ew=loption(error_words)*/
+ { match f@b(*@ew*) with
[] -> raise (Semantic_cocci.Semantic "minus slice can't be empty")
| code -> Top_level.top_level code }
-plus_body:
+plus_body:
f=loption(filespec)
b=loption(plus_start)
- ew=loption(error_words)
- { Top_level.top_level (f@b@ew) }
+ /*ew=loption(error_words)*/
+ { Top_level.top_level (f@b(*@ew*)) }
minus_exp_body:
f=loption(filespec)
b=top_eexpr
- ew=loption(error_words)
- { match f@[b]@ew with
+ /*ew=loption(error_words)*/
+ { match f@[b](*@ew*) with
[] -> raise (Semantic_cocci.Semantic "minus slice can't be empty")
| code -> Top_level.top_level code }
plus_exp_body:
f=loption(filespec)
b=top_eexpr
- ew=loption(error_words)
- { Top_level.top_level (f@[b]@ew) }
+ /*ew=loption(error_words)*/
+ { Top_level.top_level (f@[b](*@ew*)) }
filespec:
TMinusFile TPlusFile
includes:
TIncludeL
{ Ast0.wrap
- (Ast0.Include(P.clt2mcode "#include" (P.drop_aft (P.id2clt $1)),
+ (Ast0.Include(P.clt2mcode "#include"
+ (P.drop_pos (P.drop_aft (P.id2clt $1))),
let (arity,ln,lln,offset,col,strbef,straft,pos) =
P.id2clt $1 in
let clt =
(P.drop_bef clt))) }
| TIncludeNL
{ Ast0.wrap
- (Ast0.Include(P.clt2mcode "#include" (P.drop_aft (P.id2clt $1)),
+ (Ast0.Include(P.clt2mcode "#include"
+ (P.drop_pos (P.drop_aft (P.id2clt $1))),
let (arity,ln,lln,offset,col,strbef,straft,pos) =
P.id2clt $1 in
let clt =
Ast0.wrap Ast0.NoParams,
body)) }
| TDefineParam define_param_list_option TCPar
- { let (clt,ident,parenoff) = $1 in
+ { let (clt,ident,parenoff,parencol) = $1 in
+ (* clt is the start of the #define itself *)
let (arity,line,lline,offset,col,strbef,straft,pos) = clt in
let lp =
- P.clt2mcode "(" (arity,line,lline,parenoff,0,[],[],Ast0.NoMetaPos) in
+ P.clt2mcode "("
+ (arity,line,lline,parenoff,parencol,[],[],Ast0.NoMetaPos) in
function body ->
Ast0.wrap
(Ast0.Define
(Ast0.FunctionType(Some t,
P.clt2mcode "(" lp, d, P.clt2mcode ")" rp)),
id, P.clt2mcode ";" pt)) }
-| s=ioption(storage) t=Tvoid
- id=func_ident lp=TOPar d=decl_list(name_opt_decl) rp=TCPar pt=TPtVirg
- { let t = Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.VoidType t, None)) in
- Ast0.wrap
- (Ast0.UnInit
- (s,
- Ast0.wrap
- (Ast0.FunctionType(Some t,
- P.clt2mcode "(" lp, d, P.clt2mcode ")" rp)),
- id, P.clt2mcode ";" pt)) }
-
fundecl:
f=fninfo
TFunDecl i=func_ident lp=TOPar d=decl_list(decl) rp=TCPar
lb=TOBrace b=fun_start rb=TCBrace
- { Ast0.wrap(Ast0.FunDecl((Ast0.default_info(),Ast0.context_befaft()),
+ { P.verify_parameter_declarations (Ast0.undots d);
+ Ast0.wrap(Ast0.FunDecl((Ast0.default_info(),Ast0.context_befaft()),
f, i,
P.clt2mcode "(" lp, d,
P.clt2mcode ")" rp,
List.find (function Ast0.FStorage(_) -> true | _ -> false) $2 in
raise (Semantic_cocci.Semantic "duplicate storage")
with Not_found -> (Ast0.FStorage($1))::$2 }
- | t=fn_ctype r=fninfo_nt { (Ast0.FType(t))::r }
+ | t=ctype r=fninfo_nt { (Ast0.FType(t))::r }
| Tinline fninfo
{ try
let _ = List.find (function Ast0.FInline(_) -> true | _ -> false) $2 in
decl: t=ctype i=ident
{ Ast0.wrap(Ast0.Param(t, Some i)) }
- | t=fn_ctype lp=TOPar s=TMul i=ident rp=TCPar
+ | t=ctype { (*verify in FunDecl*) Ast0.wrap(Ast0.Param(t, None)) }
+ | t=ctype lp=TOPar s=TMul i=ident rp=TCPar
lp1=TOPar d=decl_list(name_opt_decl) rp1=TCPar
{ let fnptr =
Ast0.wrap
(t,P.clt2mcode "(" lp,P.clt2mcode "*" s,P.clt2mcode ")" rp,
P.clt2mcode "(" lp1,d,P.clt2mcode ")" rp1)) in
Ast0.wrap(Ast0.Param(fnptr, Some i)) }
- | t=Tvoid
- { let ty = Ast0.wrap(Ast0.BaseType(P.clt2mcode Ast.VoidType t, None)) in
- Ast0.wrap(Ast0.VoidParam(ty)) }
| TMetaParam
{ let (nm,pure,clt) = $1 in
Ast0.wrap(Ast0.MetaParam(P.clt2mcode nm clt,pure)) }
name_opt_decl:
decl { $1 }
- | t=ctype { Ast0.wrap(Ast0.Param(t, None)) }
- | t=fn_ctype lp=TOPar s=TMul rp=TCPar
+ | t=ctype lp=TOPar s=TMul rp=TCPar
lp1=TOPar d=decl_list(name_opt_decl) rp1=TCPar
{ let fnptr =
Ast0.wrap
{ P.doloop $1 $2 $3 $4 $5 $6 $7 }
| iter_ident TOPar eexpr_list_option TCPar single_statement
{ P.iterator $1 $2 $3 $4 $5 }
-| TSwitch TOPar eexpr TCPar TOBrace list(case_line) TCBrace
- { P.switch $1 $2 $3 $4 $5 $6 $7 }
+| TSwitch TOPar eexpr TCPar TOBrace list(decl_var) list(case_line) TCBrace
+ { P.switch $1 $2 $3 $4 $5 (List.concat $6) $7 $8 }
| TReturn eexpr TPtVirg { P.ret_exp $1 $2 $3 }
| TReturn TPtVirg { P.ret $1 $2 }
| TBreak TPtVirg { P.break $1 $2 }
{ Ast0.wrap(Ast0.Nest(P.clt2mcode "<+..." $1, b,
P.clt2mcode "...+>" c, List.concat w, true)) }
-whenppdecs: w=whens(when_start,rule_elem_statement)
+%inline stm_dots_ell:
+ a=TEllipsis w=list(whenppdecs)
+ { Ast0.wrap(Ast0.Dots(P.clt2mcode "..." a, List.concat w)) }
+
+%inline stm_dots_nest:
+ a=TOEllipsis w=list(whenppdecs) b=nest_start c=TCEllipsis
+ { Ast0.wrap(Ast0.Nest(P.clt2mcode "<..." a, b,
+ P.clt2mcode "...>" c, List.concat w, false)) }
+| a=TPOEllipsis w=list(whenppdecs) b=nest_start c=TPCEllipsis
+ { Ast0.wrap(Ast0.Nest(P.clt2mcode "<+..." a, b,
+ P.clt2mcode "...+>" c, List.concat w, true)) }
+
+whenppdecs: w=whens(when_start,rule_elem_statement,any_strict)
{ w }
/* a statement that fits into a single rule_elem. should nests be included?
case_line:
TDefault TDotDot fun_start
- { Ast0.wrap(Ast0.Default(P.clt2mcode "default" $1,P.clt2mcode ":" $2,$3)) }
+ { Ast0.wrap
+ (Ast0.Default(P.clt2mcode "default" $1,P.clt2mcode ":" $2,$3)) }
| TCase eexpr TDotDot fun_start
{ Ast0.wrap(Ast0.Case(P.clt2mcode "case" $1,$2,P.clt2mcode ":" $3,$4)) }
+/* | lp=TOPar0 t=midzero_list(case_line,case_line) rp=TCPar0
+ { let (mids,code) = ([],[t]) in
+ Ast0.wrap
+ (Ast0.DisjCase(P.clt2mcode "(" lp,code,mids, P.clt2mcode ")" rp)) } */
/* In the following, an identifier as a type is not fully supported. Indeed,
the language is ambiguous: what is foo * bar; */
P.clt2mcode ";" pv))] }
/* function pointer type */
| s=ioption(storage)
- t=fn_ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
+ t=ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
lp2=TOPar p=decl_list(name_opt_decl) rp2=TCPar
pv=TPtVirg
{ let (id,fn) = d in
[Ast0.wrap(Ast0.UnInit(s,fn t,id,P.clt2mcode ";" pv))] }
| decl_ident TOPar eexpr_list_option TCPar TPtVirg
{ [Ast0.wrap(Ast0.MacroDecl($1,P.clt2mcode "(" $2,$3,
- P.clt2mcode ")" $4,P.clt2mcode ";" $5))] }
+ P.clt2mcode ")" $4,P.clt2mcode ";" $5))] }
| s=ioption(storage)
- t=fn_ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
+ t=ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
lp2=TOPar p=decl_list(name_opt_decl) rp2=TCPar
q=TEq e=initialize pv=TPtVirg
{ let (id,fn) = d in
(t,P.clt2mcode "(" lp1,P.clt2mcode "*" st,P.clt2mcode ")" rp1,
P.clt2mcode "(" lp2,p,P.clt2mcode ")" rp2)) in
[Ast0.wrap(Ast0.Init(s,fn t,id,P.clt2mcode "=" q,e,P.clt2mcode ";" pv))]}
- | s=Ttypedef t=ctype id=typedef_ident pv=TPtVirg
+ | s=Ttypedef t=typedef_ctype id=comma_list(typedef_ident) pv=TPtVirg
{ let s = P.clt2mcode "typedef" s in
- [Ast0.wrap(Ast0.Typedef(s,t,id,P.clt2mcode ";" pv))] }
+ List.map
+ (function id ->
+ Ast0.wrap(Ast0.Typedef(s,t,id,P.clt2mcode ";" pv)))
+ id }
one_decl_var:
t=ctype pv=TPtVirg
P.clt2mcode ";" pv)) }
/* function pointer type */
| s=ioption(storage)
- t=fn_ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
+ t=ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
lp2=TOPar p=decl_list(name_opt_decl) rp2=TCPar
pv=TPtVirg
{ let (id,fn) = d in
Ast0.wrap(Ast0.UnInit(s,fn t,id,P.clt2mcode ";" pv)) }
| decl_ident TOPar eexpr_list_option TCPar TPtVirg
{ Ast0.wrap(Ast0.MacroDecl($1,P.clt2mcode "(" $2,$3,
- P.clt2mcode ")" $4,P.clt2mcode ";" $5)) }
+ P.clt2mcode ")" $4,P.clt2mcode ";" $5)) }
| s=ioption(storage)
- t=fn_ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
+ t=ctype lp1=TOPar st=TMul d=d_ident rp1=TCPar
lp2=TOPar p=decl_list(name_opt_decl) rp2=TCPar
q=TEq e=initialize pv=TPtVirg
{ let (id,fn) = d in
{ Ast0.wrap
(Ast0.InitList(P.clt2mcode "{" $1,Ast0.wrap(Ast0.DOTS []),
P.clt2mcode "}" $2)) }
+ | TMetaInit
+ {let (nm,pure,clt) = $1 in
+ Ast0.wrap(Ast0.MetaInit(P.clt2mcode nm clt,pure)) }
initialize2:
/*arithexpr and not eexpr because can have ambiguity with comma*/
(Ast0.InitList(P.clt2mcode "{" $1,Ast0.wrap(Ast0.DOTS []),
P.clt2mcode "}" $2)) }
/* gccext:, labeled elements */
-| TDot ident TEq initialize2
- { Ast0.wrap(Ast0.InitGccDotName(P.clt2mcode "." $1,$2,P.clt2mcode "=" $3,$4)) }
+| list(designator) TEq initialize2
+ { Ast0.wrap(Ast0.InitGccExt($1,P.clt2mcode "=" $2,$3)) }
| ident TDotDot initialize2
{ Ast0.wrap(Ast0.InitGccName($1,P.clt2mcode ":" $2,$3)) } /* in old kernel */
-| TOCro eexpr TCCro TEq initialize2
- { Ast0.wrap(Ast0.InitGccIndex(P.clt2mcode "[" $1,$2,P.clt2mcode "]" $3,
- P.clt2mcode "=" $4,$5)) }
-| TOCro eexpr TEllipsis eexpr TCCro TEq initialize2
- { Ast0.wrap(Ast0.InitGccRange(P.clt2mcode "[" $1,$2,P.clt2mcode "..." $3,
- $4,P.clt2mcode "]" $5,P.clt2mcode "=" $6,$7)) }
+
+designator:
+ | TDot ident
+ { Ast0.DesignatorField (P.clt2mcode "." $1,$2) }
+ | TOCro eexpr TCCro
+ { Ast0.DesignatorIndex (P.clt2mcode "[" $1,$2,P.clt2mcode "]" $3) }
+ | TOCro eexpr TEllipsis eexpr TCCro
+ { Ast0.DesignatorRange (P.clt2mcode "[" $1,$2,P.clt2mcode "..." $3,
+ $4,P.clt2mcode "]" $5) }
initialize_list:
initialize_list_start { Ast0.wrap(Ast0.DOTS($1)) }
{ Ast0.wrap(Ast0.Infix ($2, P.clt2mcode Ast.Inc $1)) }
| TDec unary_expr(r,pe)
{ Ast0.wrap(Ast0.Infix ($2, P.clt2mcode Ast.Dec $1)) }
- | unary_op unary_expr(r,pe)
+ | unary_op cast_expr(r,pe)
{ let mcode = $1 in Ast0.wrap(Ast0.Unary($2, mcode)) }
| TBang unary_expr(r,pe)
{ let mcode = P.clt2mcode Ast.Not $1 in
pure_ident:
TIdent { $1 }
+pure_ident_kwd:
+ | TIdentifier { "identifier" }
+ | TExpression { "expression" }
+ | TStatement { "statement" }
+ | TFunction { "function" }
+ | TLocal { "local" }
+ | TType { "type" }
+ | TParameter { "parameter" }
+ | TIdExpression { "idexpression" }
+ | TInitialiser { "initialiser" }
+ | Tlist { "list" }
+ | TFresh { "fresh" }
+ | TConstant { "constant" }
+ | TError { "error" }
+ | TWords { "words" }
+ | TPure { "pure" }
+ | TContext { "context" }
+ | TGenerated { "generated" }
+ | TTypedef { "typedef" }
+ | TDeclarer { "declarer" }
+ | TIterator { "iterator" }
+ | TName { "name" }
+ | TPosition { "position" }
+
meta_ident:
- TRuleName TDot pure_ident { (Some $1,P.id2name $3) }
+ TRuleName TDot pure_ident { (Some $1,P.id2name $3) }
+ | TRuleName TDot pure_ident_kwd { (Some $1,$3) }
pure_ident_or_meta_ident:
pure_ident { (None,P.id2name $1) }
+ | pure_ident_kwd { (None,$1) }
| meta_ident { $1 }
- | Tlist { (None,"list") }
- | TError { (None,"error") }
- | TType { (None,"type") }
-pure_ident_or_meta_ident_with_not_eq(not_eq):
- i=pure_ident_or_meta_ident l=loption(not_eq) { (i,l) }
+pure_ident_or_meta_ident_with_seed:
+ pure_ident_or_meta_ident { ($1,Ast.NoVal) }
+ | pure_ident_or_meta_ident TEq
+ separated_nonempty_list(TCppConcatOp,seed_elem)
+ { match $3 with
+ [Ast.SeedString s] -> ($1,Ast.StringSeed s)
+ | _ -> ($1,Ast.ListSeed $3) }
+
+seed_elem:
+ TString { let (x,_) = $1 in Ast.SeedString x }
+| TMetaId { let (x,_,_,_) = $1 in Ast.SeedId x }
+| TRuleName TDot pure_ident
+ { let nm = ($1,P.id2name $3) in
+ P.check_meta(Ast.MetaIdDecl(Ast.NONE,nm));
+ Ast.SeedId nm }
+
+pure_ident_or_meta_ident_with_x_eq(x_eq):
+ i=pure_ident_or_meta_ident l=loption(x_eq)
+ {
+ (i, l)
+ }
+
+pure_ident_or_meta_ident_with_econstraint(x_eq):
+ i=pure_ident_or_meta_ident optc=option(x_eq)
+ {
+ match optc with
+ None -> (i, Ast0.NoConstraint)
+ | Some c -> (i, c)
+ }
+
+pure_ident_or_meta_ident_with_idconstraint_virt(constraint_type):
+ i=pure_ident_or_meta_ident c=option(constraint_type)
+ {
+ Common.Left
+ (match c with
+ None -> (i, Ast.IdNoConstraint)
+ | Some constraint_ -> (i,constraint_))
+ }
+| TVirtual TDot pure_ident { Common.Right (P.id2name $3) }
+
+pure_ident_or_meta_ident_with_idconstraint(constraint_type):
+ i=pure_ident_or_meta_ident c=option(constraint_type)
+ {
+ match c with
+ None -> (i, Ast.IdNoConstraint)
+ | Some constraint_ -> (i,constraint_)
+ }
+
+re_or_not_eqid:
+ re=regexp_eqid {re}
+ | ne=not_eqid {ne}
+
+regexp_eqid:
+ TTildeEq re=TString
+ { (if !Data.in_iso
+ then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
+ let (s,_) = re in Ast.IdRegExp (s,Str.regexp s)
+ }
+ | TTildeExclEq re=TString
+ { (if !Data.in_iso
+ then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
+ let (s,_) = re in Ast.IdNotRegExp (s,Str.regexp s)
+ }
-not_eq:
+not_eqid:
TNotEq i=pure_ident
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
- [Ast0.wrap(Ast0.Id(P.id2mcode i))] }
+ (if !Data.in_generating
+ (* pb: constraints not stored with metavars; too lazy to search for
+ them in the pattern *)
+ then failwith "constraints not allowed in a generated rule file");
+ Ast.IdNegIdSet([fst i]) }
| TNotEq TOBrace l=comma_list(pure_ident) TCBrace
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
- List.map (function i -> Ast0.wrap(Ast0.Id(P.id2mcode i))) l }
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
+ Ast.IdNegIdSet(List.map fst l)
+ }
+
+re_or_not_eqe:
+ re=regexp_eqid {Ast0.NotIdCstrt (re)}
+ | ne=not_eqe {Ast0.NotExpCstrt (ne)}
not_eqe:
TNotEq i=pure_ident
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
- [Ast0.wrap(Ast0.Ident(Ast0.wrap(Ast0.Id(P.id2mcode i))))] }
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
+ [Ast0.wrap(Ast0.Ident(Ast0.wrap(Ast0.Id(P.id2mcode i))))]
+ }
| TNotEq TOBrace l=comma_list(pure_ident) TCBrace
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
List.map
(function i ->
- Ast0.wrap(Ast0.Ident(Ast0.wrap(Ast0.Id(P.id2mcode i)))))
- l }
+ Ast0.wrap(Ast0.Ident(Ast0.wrap(Ast0.Id(P.id2mcode i)))))
+ l
+ }
not_ceq:
TNotEq i=ident_or_const
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
[i] }
| TNotEq TOBrace l=comma_list(ident_or_const) TCBrace
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
l }
ident_or_const:
TNotEq i=meta_ident
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
match i with
(None,_) -> failwith "constraint must be an inherited variable"
| (Some rule,name) ->
| TNotEq TOBrace l=comma_list(meta_ident) TCBrace
{ (if !Data.in_iso
then failwith "constraints not allowed in iso file");
+ (if !Data.in_generating
+ then failwith "constraints not allowed in a generated rule file");
List.map
(function
(None,_) ->
/*****************************************************************************/
decl_list(decl):
- decl_list_start(decl)
+ /* empty */ { Ast0.wrap(Ast0.DOTS([])) }
+| decl_list_start(decl)
{let circle x =
match Ast0.unwrap x with Ast0.Pcircles(_) -> true | _ -> false in
if List.exists circle $1
Some nm -> Some(P.clt2mcode nm clt)
| None -> None in
Ast0.wrap(Ast0.MetaParamList(nm,lenname,pure)) }
-
+
comma_decls(dotter,decl):
TComma dotter
{ function dot_builder ->
/* ---------------------------------------------------------------------- */
-error_words:
+/* error words make it complicated to be able to use error as a metavariable
+name or a type in a metavariable list; for that we would like to allow TError
+as an ident, but that makes conflicts with this rule. To add back error words,
+need to find some appropriate delimiter for it, but it has not been used much
+so just drop it */
+/*error_words:
TError TWords TEq TOCro cl=comma_list(dexpr) TCCro
{ [Ast0.wrap(Ast0.ERRORWORDS(cl))] }
+*/
/* ---------------------------------------------------------------------- */
/* sequences of statements and expressions */
fundecl { [Ast0.wrap(Ast0.DECL($1))] }
| ctype { [Ast0.wrap(Ast0.OTHER(Ast0.wrap(Ast0.Ty($1))))] }
| top_init { [Ast0.wrap(Ast0.OTHER(Ast0.wrap(Ast0.TopInit($1))))] }
-| toplevel_seq_start(toplevel_after_dots_init)
+| toplevel_seq_startne(toplevel_after_dots_init)
{ List.map (function x -> Ast0.wrap(Ast0.OTHER(x))) $1 }
+toplevel_seq_startne(after_dots_init):
+ a=stm_dots_ell b=after_dots_init { a::b }
+| a=stm_dots_nest b=after_dots_init { a::b }
+| a=stm_dots_nest { [a] }
+| expr toplevel_after_exp { (Ast0.wrap(Ast0.Exp($1)))::$2 }
+| decl_statement_expr toplevel_after_stm { $1@$2 }
+
toplevel_seq_start(after_dots_init):
stm_dots after_dots_init { $1::$2 }
| expr toplevel_after_exp { (Ast0.wrap(Ast0.Exp($1)))::$2 }
d=dotter { (d,None) }
| d=dotter TWhen TNotEq w=when_grammar TLineEnd { (d,Some w) }
-whens(when_grammar,simple_when_grammar):
+whens(when_grammar,simple_when_grammar,any_strict):
TWhen TNotEq w=when_grammar TLineEnd { [Ast0.WhenNot w] }
| TWhen TEq w=simple_when_grammar TLineEnd { [Ast0.WhenAlways w] }
| TWhen comma_list(any_strict) TLineEnd
| TPArob TMetaPos { () }
| TScriptData { () }
-script_meta_main: py=pure_ident TShOp TRuleName TDot cocci=pure_ident TMPtVirg
+script_meta_main:
+ py=pure_ident TShOp TRuleName TDot cocci=pure_ident TMPtVirg
{ (P.id2name py, ($3, P.id2name cocci)) }
+ | py=pure_ident TShOp TVirtual TDot cocci=pure_ident TMPtVirg
+ { (P.id2name py, ("virtual", P.id2name cocci)) }