Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

lustrec / src / parser_lustre.mly @ 89b9e25c

History | View | Annotate | Download (16.6 KB)

1
/* ----------------------------------------------------------------------------
2
 * SchedMCore - A MultiCore Scheduling Framework
3
 * Copyright (C) 2009-2011, ONERA, Toulouse, FRANCE - LIFL, Lille, FRANCE
4
 *
5
 * This file is part of Prelude
6
 *
7
 * Prelude is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public License
9
 * as published by the Free Software Foundation ; either version 2 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * Prelude is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY ; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this program ; if not, write to the Free Software
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20
 * USA
21
 *---------------------------------------------------------------------------- */
22

    
23
%{
24
open LustreSpec
25
open Corelang
26
open Dimension
27
open Utils
28

    
29
let mktyp x = mktyp (Location.symbol_rloc ()) x
30
let mkclock x = mkclock (Location.symbol_rloc ()) x
31
let mkvar_decl x = mkvar_decl (Location.symbol_rloc ()) x
32
let mkexpr x = mkexpr (Location.symbol_rloc ()) x
33
let mkeq x = mkeq (Location.symbol_rloc ()) x
34
let mkassert x = mkassert (Location.symbol_rloc ()) x
35
let mktop_decl x = mktop_decl (Location.symbol_rloc ()) x
36
let mkpredef_call x = mkpredef_call (Location.symbol_rloc ()) x
37
let mkpredef_unary_call x = mkpredef_unary_call (Location.symbol_rloc ()) x
38

    
39
let mkdim_int i = mkdim_int (Location.symbol_rloc ()) i
40
let mkdim_bool b = mkdim_bool (Location.symbol_rloc ()) b
41
let mkdim_ident id = mkdim_ident (Location.symbol_rloc ()) id
42
let mkdim_appl f args = mkdim_appl (Location.symbol_rloc ()) f args
43
let mkdim_ite i t e = mkdim_ite (Location.symbol_rloc ()) i t e
44

    
45
let add_node own msg hashtbl name value =
46
  try
47
    match (Hashtbl.find hashtbl name).top_decl_desc, value.top_decl_desc with
48
    | Node _        , ImportedNode _ when own
49
                        ->
50
       Hashtbl.add hashtbl name value
51
    | ImportedNode _, _ ->
52
       Hashtbl.add hashtbl name value
53
    | Node _        , _ -> 
54
       raise (Corelang.Error (Corelang.Already_bound_symbol msg, Location.symbol_rloc ()))
55
    | _                 -> assert false
56
  with
57
    Not_found ->
58
       Hashtbl.add hashtbl name value
59

    
60
let add_symbol msg hashtbl name value =
61
 if Hashtbl.mem hashtbl name
62
 then raise (Corelang.Error (Corelang.Already_bound_symbol msg, Location.symbol_rloc ()))
63
 else Hashtbl.add hashtbl name value
64

    
65
let check_symbol msg hashtbl name =
66
 if not (Hashtbl.mem hashtbl name)
67
 then raise (Corelang.Error (Corelang.Unbound_symbol msg, Location.symbol_rloc ()))
68
 else ()
69

    
70
%}
71

    
72
%token <int> INT
73
%token <string> REAL
74
%token <float> FLOAT
75
%token AUTOMATON STATE UNTIL UNLESS RESTART RESUME LAST
76
%token STATELESS ASSERT OPEN QUOTE FUNCTION
77
%token <string> IDENT
78
%token <LustreSpec.expr_annot> ANNOT
79
%token <LustreSpec.node_annot> NODESPEC
80
%token LBRACKET RBRACKET LCUR RCUR LPAR RPAR SCOL COL COMMA COLCOL 
81
%token AMPERAMPER BARBAR NOT POWER
82
%token IF THEN ELSE
83
%token UCLOCK DCLOCK PHCLOCK TAIL
84
%token MERGE FBY WHEN WHENNOT EVERY
85
%token NODE LET TEL RETURNS VAR IMPORTED SENSOR ACTUATOR WCET TYPE CONST
86
%token STRUCT ENUM
87
%token TINT TFLOAT TREAL TBOOL TCLOCK
88
%token RATE DUE
89
%token EQ LT GT LTE GTE NEQ
90
%token AND OR XOR IMPL
91
%token MULT DIV MOD
92
%token MINUS PLUS UMINUS
93
%token PRE ARROW
94

    
95
%token EOF
96

    
97
%nonassoc COMMA
98
%left MERGE IF
99
%nonassoc ELSE
100
%right ARROW FBY
101
%left WHEN WHENNOT UCLOCK DCLOCK PHCLOCK
102
%right COLCOL
103
%right IMPL
104
%left OR XOR BARBAR
105
%left AND AMPERAMPER
106
%left NOT
107
%nonassoc INT
108
%nonassoc EQ LT GT LTE GTE NEQ
109
%left MINUS PLUS
110
%left MULT DIV MOD
111
%left UMINUS
112
%left POWER
113
%left PRE LAST
114
%nonassoc RBRACKET
115
%nonassoc LBRACKET
116

    
117
%start prog
118
%type <Corelang.top_decl list> prog
119
%start header
120
%type <bool -> Corelang.top_decl list> header
121

    
122
%%
123

    
124
prog:
125
 open_list typ_def_list top_decl_list EOF { $1 @ (List.rev $3) }
126

    
127
header:
128
 open_list typ_def_list top_decl_header_list EOF { (fun own -> ($1 @ (List.rev ($3 own)))) }
129

    
130
open_list:
131
  { [] }
132
| open_lusi open_list { $1 :: $2 }
133

    
134
open_lusi:
135
  OPEN QUOTE IDENT QUOTE { mktop_decl (Open $3) }
136

    
137
top_decl_list:
138
  top_decl {[$1]}
139
| top_decl_list top_decl {$2::$1}
140

    
141

    
142
top_decl_header_list:
143
  top_decl_header {(fun own -> [$1 own]) }
144
| top_decl_header_list top_decl_header {(fun own -> ($2 own)::($1 own)) }
145

    
146

    
147
top_decl_header:
148
| NODE IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR stateless_opt SCOL
149
    {let nd = mktop_decl (ImportedNode
150
                            {nodei_id = $2;
151
                             nodei_type = Types.new_var ();
152
                             nodei_clock = Clocks.new_var true;
153
                             nodei_inputs = List.rev $4;
154
                             nodei_outputs = List.rev $9;
155
			     nodei_stateless = $12;
156
			     nodei_spec = None})
157
    in
158
    (fun own -> add_node own ("node " ^ $2) node_table $2 nd; nd) }
159

    
160
| nodespec_list NODE IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR stateless_opt SCOL
161
    {let nd = mktop_decl (ImportedNode
162
                            {nodei_id = $3;
163
                             nodei_type = Types.new_var ();
164
                             nodei_clock = Clocks.new_var true;
165
                             nodei_inputs = List.rev $5;
166
                             nodei_outputs = List.rev $10;
167
			     nodei_stateless = $13;
168
			     nodei_spec = Some $1})
169
    in
170
    (fun own -> add_node own ("node " ^ $3) node_table $3 nd; nd) }
171

    
172
| FUNCTION IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL
173
    {let nd = mktop_decl (ImportedNode
174
                            {nodei_id = $2;
175
                             nodei_type = Types.new_var ();
176
			     nodei_clock = Clocks.new_var true;
177
                             nodei_inputs = List.rev $4;
178
                             nodei_outputs = List.rev $9;
179
			     nodei_stateless = true;
180
			     nodei_spec = None})
181
     in
182
     (fun own -> add_node own ("function " ^ $2) node_table $2 nd; nd) }
183

    
184
| nodespec_list FUNCTION IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL
185
    {let nd = mktop_decl (ImportedNode
186
                            {nodei_id = $3;
187
                             nodei_type = Types.new_var ();
188
			     nodei_clock = Clocks.new_var true;
189
                             nodei_inputs = List.rev $5;
190
                             nodei_outputs = List.rev $10;
191
			     nodei_stateless = true;
192
			     nodei_spec = Some $1})
193
     in
194
    (fun own -> add_node own ("function " ^ $3) node_table $3 nd; nd) }
195

    
196
top_decl:
197
| CONST cdecl_list { mktop_decl (Consts (List.rev $2)) }
198

    
199
| NODE IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt locals LET eq_list TEL 
200
    {let eqs, asserts, annots = $15 in
201
     let nd = mktop_decl (Node
202
                            {node_id = $2;
203
                             node_type = Types.new_var ();
204
                             node_clock = Clocks.new_var true;
205
                             node_inputs = List.rev $4;
206
                             node_outputs = List.rev $9;
207
                             node_locals = List.rev $13;
208
			     node_gencalls = [];
209
			     node_checks = [];
210
			     node_asserts = asserts; 
211
                             node_eqs = eqs;
212
			     node_spec = None;
213
			     node_annot = match annots with [] -> None | _ -> Some annots})
214
    in
215
    add_node true ("node " ^ $2) node_table $2 nd; nd}
216

    
217
| nodespec_list NODE IDENT LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt locals LET eq_list TEL 
218
    {let eqs, asserts, annots = $16 in
219
     let nd = mktop_decl (Node
220
                            {node_id = $3;
221
                             node_type = Types.new_var ();
222
                             node_clock = Clocks.new_var true;
223
                             node_inputs = List.rev $5;
224
                             node_outputs = List.rev $10;
225
                             node_locals = List.rev $14;
226
			     node_gencalls = [];
227
			     node_checks = [];
228
			     node_asserts = asserts; 
229
                             node_eqs = eqs;
230
			     node_spec = Some $1;
231
			     node_annot = match annots with [] -> None | _ -> Some annots})
232
    in
233
    add_node true ("node " ^ $3) node_table $3 nd; nd}
234

    
235
nodespec_list:
236
NODESPEC { $1 }
237
| NODESPEC nodespec_list { LustreSpec.merge_node_annot $1 $2 }
238

    
239
stateless_opt:
240
   { false }
241
| STATELESS {true}
242

    
243
typ_def_list:
244
    /* empty */ {}
245
| typ_def SCOL typ_def_list {$1;$3}
246

    
247
typ_def:
248
  TYPE IDENT EQ typeconst {
249
    try
250
      add_symbol ("type " ^ $2) type_table (Tydec_const $2) (Corelang.get_repr_type $4)
251
    with Not_found-> assert false }
252
| TYPE IDENT EQ ENUM LCUR tag_list RCUR { Hashtbl.add type_table (Tydec_const $2) (Tydec_enum ($6 (Tydec_const $2))) }
253
| TYPE IDENT EQ STRUCT LCUR field_list RCUR { Hashtbl.add type_table (Tydec_const $2) (Tydec_struct ($6 (Tydec_const $2))) }
254

    
255
array_typ_decl:
256
                            { fun typ -> typ }
257
 | POWER dim array_typ_decl { fun typ -> $3 (Tydec_array ($2, typ)) }
258

    
259
typeconst:
260
  TINT array_typ_decl  { $2 Tydec_int }
261
| TBOOL array_typ_decl { $2 Tydec_bool  }
262
| TREAL array_typ_decl { $2 Tydec_real  }
263
| TFLOAT array_typ_decl { $2 Tydec_float }
264
| IDENT array_typ_decl { check_symbol ("type " ^ $1) type_table (Tydec_const $1); $2 (Tydec_const $1) }
265
| TBOOL TCLOCK  { Tydec_clock Tydec_bool }
266
| IDENT TCLOCK  { Tydec_clock (Tydec_const $1) }
267

    
268
tag_list:
269
  IDENT
270
  { (fun t -> add_symbol ("tag " ^ $1) tag_table $1 t; $1 :: []) }
271
| tag_list COMMA IDENT
272
  { (fun t -> add_symbol ("tag " ^ $3)tag_table $3 t; $3 :: ($1 t)) }
273

    
274
field_list:
275
  { (fun t -> []) }
276
| field_list IDENT COL typeconst SCOL
277
  { (fun t -> add_symbol ("field " ^ $2) field_table $2 t; ($1 t) @ [ ($2, $4) ]) }
278

    
279
eq_list:
280
  { [], [], [] }
281
| eq eq_list {let eql, assertl, annotl = $2 in ($1::eql), assertl, annotl}
282
| assert_ eq_list {let eql, assertl, annotl = $2 in eql, ($1::assertl), annotl}
283
| ANNOT eq_list {let eql, assertl, annotl = $2 in eql, assertl, $1@annotl}
284
| automaton eq_list {let eql, assertl, annotl = $2 in ($1::eql), assertl, annotl}
285

    
286
automaton:
287
 AUTOMATON IDENT handler_list { failwith "not implemented" }
288

    
289
handler_list:
290
     { [] }
291
| handler handler_list { $1::$2 }
292

    
293
handler:
294
 STATE IDENT ARROW unless_list locals LET eq_list TEL until_list { () }
295

    
296
unless_list:
297
    { [] }
298
| unless unless_list { $1::$2 }
299

    
300
until_list:
301
    { [] }
302
| until until_list { $1::$2 }
303

    
304
unless:
305
  UNLESS expr RESTART IDENT { }
306
| UNLESS expr RESUME IDENT  { }
307

    
308
until:
309
  UNTIL expr RESTART IDENT { }
310
| UNTIL expr RESUME IDENT  { }
311

    
312
assert_:
313
| ASSERT expr SCOL {mkassert ($2)}
314

    
315
eq:
316
       ident_list      EQ expr SCOL {mkeq (List.rev $1,$3)}
317
| LPAR ident_list RPAR EQ expr SCOL {mkeq (List.rev $2,$5)}
318

    
319
tuple_expr:
320
    expr COMMA expr {[$3;$1]}
321
| tuple_expr COMMA expr {$3::$1}
322

    
323
// Same as tuple expr but accepting lists with single element
324
array_expr:
325
  expr {[$1]}
326
| expr COMMA array_expr {$1::$3}
327

    
328
dim_list:
329
  dim RBRACKET { fun base -> mkexpr (Expr_access (base, $1)) }
330
| dim RBRACKET LBRACKET dim_list { fun base -> $4 (mkexpr (Expr_access (base, $1))) }
331

    
332
expr:
333
/* constants */
334
  INT {mkexpr (Expr_const (Const_int $1))}
335
| REAL {mkexpr (Expr_const (Const_real $1))}
336
| FLOAT {mkexpr (Expr_const (Const_float $1))}
337
/* Idents or type enum tags */
338
| IDENT {
339
  if Hashtbl.mem tag_table $1
340
  then mkexpr (Expr_const (Const_tag $1))
341
  else mkexpr (Expr_ident $1)}
342
| LPAR ANNOT expr RPAR
343
    {update_expr_annot $3 $2}
344
| LPAR expr RPAR
345
    {$2}
346
| LPAR tuple_expr RPAR
347
    {mkexpr (Expr_tuple (List.rev $2))}
348

    
349
/* Array expressions */
350
| LBRACKET array_expr RBRACKET { mkexpr (Expr_array $2) }
351
| expr POWER dim { mkexpr (Expr_power ($1, $3)) }
352
| expr LBRACKET dim_list { $3 $1 }
353

    
354
/* Temporal operators */
355
| PRE expr 
356
    {mkexpr (Expr_pre $2)}
357
| expr ARROW expr 
358
    {mkexpr (Expr_arrow ($1,$3))}
359
| expr FBY expr 
360
    {(*mkexpr (Expr_fby ($1,$3))*)
361
      mkexpr (Expr_arrow ($1, mkexpr (Expr_pre $3)))}
362
| expr WHEN IDENT 
363
    {mkexpr (Expr_when ($1,$3,tag_true))}
364
| expr WHENNOT IDENT
365
    {mkexpr (Expr_when ($1,$3,tag_false))}
366
| expr WHEN IDENT LPAR IDENT RPAR
367
    {mkexpr (Expr_when ($1, $5, $3))}
368
| MERGE IDENT handler_expr_list
369
    {mkexpr (Expr_merge ($2,$3))}
370

    
371
/* Applications */
372
| IDENT LPAR expr RPAR
373
    {mkexpr (Expr_appl ($1, $3, None))}
374
| IDENT LPAR expr RPAR EVERY IDENT
375
    {mkexpr (Expr_appl ($1, $3, Some ($6, tag_true)))}
376
| IDENT LPAR expr RPAR EVERY IDENT LPAR IDENT RPAR
377
    {mkexpr (Expr_appl ($1, $3, Some ($8, $6))) }
378
| IDENT LPAR tuple_expr RPAR
379
    {mkexpr (Expr_appl ($1, mkexpr (Expr_tuple (List.rev $3)), None))}
380
| IDENT LPAR tuple_expr RPAR EVERY IDENT
381
    {mkexpr (Expr_appl ($1, mkexpr (Expr_tuple (List.rev $3)), Some ($6, tag_true))) }
382
| IDENT LPAR tuple_expr RPAR EVERY IDENT LPAR IDENT RPAR
383
    {mkexpr (Expr_appl ($1, mkexpr (Expr_tuple (List.rev $3)), Some ($8, $6))) }
384

    
385
/* Boolean expr */
386
| expr AND expr 
387
    {mkpredef_call "&&" [$1;$3]}
388
| expr AMPERAMPER expr 
389
    {mkpredef_call "&&" [$1;$3]}
390
| expr OR expr 
391
    {mkpredef_call "||" [$1;$3]}
392
| expr BARBAR expr 
393
    {mkpredef_call "||" [$1;$3]}
394
| expr XOR expr 
395
    {mkpredef_call "xor" [$1;$3]}
396
| NOT expr 
397
    {mkpredef_unary_call "not" $2}
398
| expr IMPL expr 
399
    {mkpredef_call "impl" [$1;$3]}
400

    
401
/* Comparison expr */
402
| expr EQ expr 
403
    {mkpredef_call "=" [$1;$3]}
404
| expr LT expr 
405
    {mkpredef_call "<" [$1;$3]}
406
| expr LTE expr 
407
    {mkpredef_call "<=" [$1;$3]}
408
| expr GT expr 
409
    {mkpredef_call ">" [$1;$3]}
410
| expr GTE  expr 
411
    {mkpredef_call ">=" [$1;$3]}
412
| expr NEQ expr 
413
    {mkpredef_call "!=" [$1;$3]}
414

    
415
/* Arithmetic expr */
416
| expr PLUS expr 
417
    {mkpredef_call "+" [$1;$3]}
418
| expr MINUS expr 
419
    {mkpredef_call "-" [$1;$3]}
420
| expr MULT expr 
421
    {mkpredef_call "*" [$1;$3]}
422
| expr DIV expr 
423
    {mkpredef_call "/" [$1;$3]}
424
| MINUS expr %prec UMINUS
425
  {mkpredef_unary_call "uminus" $2}
426
| expr MOD expr 
427
    {mkpredef_call "mod" [$1;$3]}
428

    
429
/* If */
430
| IF expr THEN expr ELSE expr
431
    {mkexpr (Expr_ite ($2, $4, $6))}
432

    
433
handler_expr_list:
434
   { [] }
435
| handler_expr handler_expr_list { $1 :: $2 }
436

    
437
handler_expr:
438
 LPAR IDENT ARROW expr RPAR { ($2, $4) }
439

    
440
signed_const_array:
441
| signed_const { [$1] }
442
| signed_const COMMA signed_const_array { $1 :: $3 }
443

    
444
signed_const_struct:
445
| IDENT EQ signed_const { [ ($1, $3) ] }
446
| IDENT EQ signed_const COMMA signed_const_struct { ($1, $3) :: $5 }
447

    
448
signed_const:
449
  INT {Const_int $1}
450
| REAL {Const_real $1}
451
| FLOAT {Const_float $1}
452
| IDENT {Const_tag $1}
453
| MINUS INT {Const_int (-1 * $2)}
454
| MINUS REAL {Const_real ("-" ^ $2)}
455
| MINUS FLOAT {Const_float (-1. *. $2)}
456
| LCUR signed_const_struct RCUR { Const_struct $2 }
457
| LBRACKET signed_const_array RBRACKET { Const_array $2 }
458

    
459
dim:
460
   INT { mkdim_int $1 }
461
| LPAR dim RPAR { $2 }
462
| IDENT { mkdim_ident $1 }
463
| dim AND dim 
464
    {mkdim_appl "&&" [$1;$3]}
465
| dim AMPERAMPER dim 
466
    {mkdim_appl "&&" [$1;$3]}
467
| dim OR dim 
468
    {mkdim_appl "||" [$1;$3]}
469
| dim BARBAR dim 
470
    {mkdim_appl "||" [$1;$3]}
471
| dim XOR dim 
472
    {mkdim_appl "xor" [$1;$3]}
473
| NOT dim 
474
    {mkdim_appl "not" [$2]}
475
| dim IMPL dim 
476
    {mkdim_appl "impl" [$1;$3]}
477

    
478
/* Comparison dim */
479
| dim EQ dim 
480
    {mkdim_appl "=" [$1;$3]}
481
| dim LT dim 
482
    {mkdim_appl "<" [$1;$3]}
483
| dim LTE dim 
484
    {mkdim_appl "<=" [$1;$3]}
485
| dim GT dim 
486
    {mkdim_appl ">" [$1;$3]}
487
| dim GTE  dim 
488
    {mkdim_appl ">=" [$1;$3]}
489
| dim NEQ dim 
490
    {mkdim_appl "!=" [$1;$3]}
491

    
492
/* Arithmetic dim */
493
| dim PLUS dim 
494
    {mkdim_appl "+" [$1;$3]}
495
| dim MINUS dim 
496
    {mkdim_appl "-" [$1;$3]}
497
| dim MULT dim 
498
    {mkdim_appl "*" [$1;$3]}
499
| dim DIV dim 
500
    {mkdim_appl "/" [$1;$3]}
501
| MINUS dim %prec UMINUS
502
  {mkdim_appl "uminus" [$2]}
503
| dim MOD dim 
504
    {mkdim_appl "mod" [$1;$3]}
505
/* If */
506
| IF dim THEN dim ELSE dim
507
    {mkdim_ite $2 $4 $6}
508

    
509
locals:
510
  {[]}
511
| VAR vdecl_list SCOL {$2}
512

    
513
vdecl_list:
514
    vdecl {$1}
515
| vdecl_list SCOL vdecl {$3 @ $1}
516

    
517
vdecl:
518
/* Useless no ?*/    ident_list
519
    {List.map mkvar_decl 
520
        (List.map (fun id -> (id, mktyp Tydec_any, mkclock Ckdec_any, false)) $1)}
521

    
522
| ident_list COL typeconst clock 
523
    {List.map mkvar_decl (List.map (fun id -> (id, mktyp $3, $4, false)) $1)}
524
| CONST ident_list COL typeconst /* static parameters don't have clocks */
525
    {List.map mkvar_decl (List.map (fun id -> (id, mktyp $4, mkclock Ckdec_any, true)) $2)}
526

    
527
cdecl_list:
528
  cdecl SCOL { [$1] }
529
| cdecl_list cdecl SCOL { $2::$1 }
530

    
531
cdecl:
532
    IDENT EQ signed_const {
533
      let c = {
534
	const_id = $1;
535
	const_loc = Location.symbol_rloc ();
536
        const_type = Types.new_var ();
537
	const_value = $3;
538
      } in
539
      Hashtbl.add consts_table $1 c; c
540
    }
541

    
542
clock:
543
    {mkclock Ckdec_any}
544
| when_list
545
    {mkclock (Ckdec_bool (List.rev $1))}
546

    
547
when_cond:
548
    WHEN IDENT {($2, tag_true)}
549
| WHENNOT IDENT {($2, tag_false)}
550
| WHEN IDENT LPAR IDENT RPAR {($4, $2)}
551

    
552
when_list:
553
    when_cond {[$1]}
554
| when_list when_cond {$2::$1}
555

    
556
ident_list:
557
  IDENT {[$1]}
558
| ident_list COMMA IDENT {$3::$1}
559

    
560
SCOL_opt:
561
    SCOL {} | {}