1 (* Bitmatch syntax extension.
2 * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 * $Id: pa_bitmatch.ml,v 1.11 2008-04-25 14:57:11 rjones Exp $
27 (* If this is true then we emit some debugging code which can
28 * be useful to tell what is happening during matches. You
29 * also need to do 'Bitmatch.debug := true' in your main program.
31 * If this is false then no extra debugging code is emitted.
35 (* A field when used in a bitmatch (a pattern). *)
37 fpatt : patt; (* field matching pattern *)
40 (* A field when used in a BITSTRING constructor (an expression). *)
42 fexpr : expr; (* field value *)
47 flen : expr; (* length in bits, may be non-const *)
48 endian : endian; (* endianness *)
49 signed : bool; (* true if signed, false if unsigned *)
51 _loc : Loc.t; (* location in source code *)
53 and endian = BigEndian | LittleEndian | NativeEndian
54 and t = Int | String | Bitstring
56 (* Generate a fresh, unique symbol each time called. *)
61 sprintf "__pabitmatch_%s_%d" name i
63 let rec parse_patt_field _loc fpatt flen qs =
64 let fpc = parse_field_common _loc flen qs in
65 { fpatt = fpatt; fpc = fpc }
67 and parse_constr_field _loc fexpr flen qs =
68 let fec = parse_field_common _loc flen qs in
69 { fexpr = fexpr; fec = fec }
71 (* Deal with the qualifiers which appear for a field of both types. *)
72 and parse_field_common _loc flen qs =
73 let endian, signed, t =
75 | None -> (None, None, None)
78 fun (endian, signed, t) q ->
81 if endian <> None then
82 Loc.raise _loc (Failure "an endian flag has been set already")
84 let endian = Some BigEndian in
88 if endian <> None then
89 Loc.raise _loc (Failure "an endian flag has been set already")
91 let endian = Some LittleEndian in
95 if endian <> None then
96 Loc.raise _loc (Failure "an endian flag has been set already")
98 let endian = Some NativeEndian in
102 if signed <> None then
103 Loc.raise _loc (Failure "a signed flag has been set already")
105 let signed = Some true in
109 if signed <> None then
110 Loc.raise _loc (Failure "a signed flag has been set already")
112 let signed = Some false in
117 Loc.raise _loc (Failure "a type flag has been set already")
124 Loc.raise _loc (Failure "a type flag has been set already")
126 let t = Some String in
131 Loc.raise _loc (Failure "a type flag has been set already")
133 let t = Some Bitstring in
137 Loc.raise _loc (Failure (s ^ ": unknown qualifier"))
138 ) (None, None, None) qs in
140 (* If type is set to string or bitstring then endianness and
141 * signedness qualifiers are meaningless and must not be set.
143 if (t = Some Bitstring || t = Some String)
144 && (endian <> None || signed <> None) then
146 Failure "string types and endian or signed qualifiers cannot be mixed"
149 (* Default endianness, signedness, type. *)
150 let endian = match endian with None -> BigEndian | Some e -> e in
151 let signed = match signed with None -> false | Some s -> s in
152 let t = match t with None -> Int | Some t -> t in
162 let string_of_endian = function
163 | BigEndian -> "bigendian"
164 | LittleEndian -> "littleendian"
165 | NativeEndian -> "nativeendian"
167 let string_of_t = function
170 | Bitstring -> "bitstring"
172 let rec string_of_patt_field { fpatt = fpatt; fpc = fpc } =
173 let fpc = string_of_field_common fpc in
176 | <:patt< $lid:id$ >> -> id
177 | _ -> "[pattern]" in
180 and string_of_constr_field { fexpr = fexpr; fec = fec } =
181 let fec = string_of_field_common fec in
184 | <:expr< $lid:id$ >> -> id
185 | _ -> "[expression]" in
188 and string_of_field_common { flen = flen;
189 endian = endian; signed = signed; t = t;
193 | <:expr< $int:i$ >> -> i
194 | _ -> "[non-const-len]" in
195 let endian = string_of_endian endian in
196 let signed = if signed then "signed" else "unsigned" in
197 let t = string_of_t t in
198 let loc_fname = Loc.file_name _loc in
199 let loc_line = Loc.start_line _loc in
200 let loc_char = Loc.start_off _loc - Loc.start_bol _loc in
202 sprintf "%s : %s, %s, %s @ (%S, %d, %d)"
203 flen t endian signed loc_fname loc_line loc_char
205 (* Generate the code for a constructor, ie. 'BITSTRING ...'. *)
206 let output_constructor _loc fields =
207 let loc_fname = Loc.file_name _loc in
208 let loc_line = string_of_int (Loc.start_line _loc) in
209 let loc_char = string_of_int (Loc.start_off _loc - Loc.start_bol _loc) in
211 (* Bitstrings are created like the 'Buffer' module (in fact, using
212 * the Buffer module), by appending snippets to a growing buffer.
213 * This is reasonably efficient and avoids a lot of garbage.
215 let buffer = gensym "buffer" in
217 (* General exception which is raised inside the constructor functions
218 * when an int expression is out of range at runtime.
220 let exn = gensym "exn" in
221 let exn_used = ref false in
223 (* Convert each field to a simple bitstring-generating expression. *)
224 let fields = List.map (
225 fun {fexpr=fexpr; fec={flen=flen; endian=endian; signed=signed;
227 (* Is flen an integer constant? If so, what is it? This
228 * is very simple-minded and only detects simple constants.
232 | <:expr< $int:i$ >> -> Some (int_of_string i)
235 let name_of_int_construct_const = function
236 (* XXX As an enhancement we should allow a 64-bit-only
237 * mode which lets us use 'int' up to 63 bits and won't
238 * compile on 32-bit platforms.
240 (* XXX The meaning of signed/unsigned breaks down at
241 * 31, 32, 63 and 64 bits.
243 | (1, _, _) -> "construct_bit"
244 | ((2|3|4|5|6|7|8), _, false) -> "construct_char_unsigned"
245 | ((2|3|4|5|6|7|8), _, true) -> "construct_char_signed"
246 | (i, BigEndian, false) when i <= 31 -> "construct_int_be_unsigned"
247 | (i, BigEndian, true) when i <= 31 -> "construct_int_be_signed"
248 | (i, LittleEndian, false) when i <= 31 -> "construct_int_le_unsigned"
249 | (i, LittleEndian, true) when i <= 31 -> "construct_int_le_signed"
250 | (i, NativeEndian, false) when i <= 31 -> "construct_int_ne_unsigned"
251 | (i, NativeEndian, true) when i <= 31 -> "construct_int_ne_signed"
252 | (32, BigEndian, false) -> "construct_int32_be_unsigned"
253 | (32, BigEndian, true) -> "construct_int32_be_signed"
254 | (32, LittleEndian, false) -> "construct_int32_le_unsigned"
255 | (32, LittleEndian, true) -> "construct_int32_le_signed"
256 | (32, NativeEndian, false) -> "construct_int32_ne_unsigned"
257 | (32, NativeEndian, true) -> "construct_int32_ne_signed"
258 | (_, BigEndian, false) -> "construct_int64_be_unsigned"
259 | (_, BigEndian, true) -> "construct_int64_be_signed"
260 | (_, LittleEndian, false) -> "construct_int64_le_unsigned"
261 | (_, LittleEndian, true) -> "construct_int64_le_signed"
262 | (_, NativeEndian, false) -> "construct_int64_ne_unsigned"
263 | (_, NativeEndian, true) -> "construct_int64_ne_signed"
265 let name_of_int_construct = function
266 (* XXX As an enhancement we should allow users to
267 * specify that a field length can fit into a char/int/int32
268 * (of course, this would have to be checked at runtime).
270 | (BigEndian, false) -> "construct_int64_be_unsigned"
271 | (BigEndian, true) -> "construct_int64_be_signed"
272 | (LittleEndian, false) -> "construct_int64_le_unsigned"
273 | (LittleEndian, true) -> "construct_int64_le_signed"
274 | (NativeEndian, false) -> "construct_int64_ne_unsigned"
275 | (NativeEndian, true) -> "construct_int64_ne_signed"
279 match t, flen_is_const with
280 (* Common case: int field, constant flen.
282 * Range checks are done inside the construction function
283 * because that's a lot simpler w.r.t. types. It might
284 * be better to move them here. XXX
286 | Int, Some i when i > 0 && i <= 64 ->
288 name_of_int_construct_const (i,endian,signed) in
292 Bitmatch.$lid:construct_func$ $lid:buffer$ $fexpr$ $flen$
297 Loc.raise _loc (Failure "length of int field must be [1..64]")
299 (* Int field, non-constant length. We need to perform a runtime
300 * test to ensure the length is [1..64].
302 * Range checks are done inside the construction function
303 * because that's a lot simpler w.r.t. types. It might
304 * be better to move them here. XXX
307 let construct_func = name_of_int_construct (endian,signed) in
311 if $flen$ >= 1 && $flen$ <= 64 then
312 Bitmatch.$lid:construct_func$ $lid:buffer$ $fexpr$ $flen$
315 raise (Bitmatch.Construct_failure
316 ("length of int field must be [1..64]",
318 $int:loc_line$, $int:loc_char$))
321 (* String, constant length > 0, must be a multiple of 8. *)
322 | String, Some i when i > 0 && i land 7 = 0 ->
323 let bs = gensym "bs" in
325 let $lid:bs$ = $fexpr$ in
326 if String.length $lid:bs$ = ($flen$ lsr 3) then
327 Bitmatch.construct_string $lid:buffer$ $lid:bs$
329 raise (Bitmatch.Construct_failure
330 ("length of string does not match declaration",
332 $int:loc_line$, $int:loc_char$))
335 (* String, constant length -1, means variable length string
338 | String, Some (-1) ->
339 <:expr< Bitmatch.construct_string $lid:buffer$ $fexpr$ >>
341 (* String, constant length = 0 is probably an error, and so is
345 Loc.raise _loc (Failure "length of string must be > 0 and a multiple of 8, or the special value -1")
347 (* String, non-constant length.
348 * We check at runtime that the length is > 0, a multiple of 8,
349 * and matches the declared length.
352 let bslen = gensym "bslen" in
353 let bs = gensym "bs" in
355 let $lid:bslen$ = $flen$ in
356 if $lid:bslen$ > 0 then (
357 if $lid:bslen$ land 7 = 0 then (
358 let $lid:bs$ = $fexpr$ in
359 if String.length $lid:bs$ = ($lid:bslen$ lsr 3) then
360 Bitmatch.construct_string $lid:buffer$ $lid:bs$
362 raise (Bitmatch.Construct_failure
363 ("length of string does not match declaration",
365 $int:loc_line$, $int:loc_char$))
367 raise (Bitmatch.Construct_failure
368 ("length of string must be a multiple of 8",
370 $int:loc_line$, $int:loc_char$))
372 raise (Bitmatch.Construct_failure
373 ("length of string must be > 0",
375 $int:loc_line$, $int:loc_char$))
378 (* Bitstring, constant length > 0. *)
379 | Bitstring, Some i when i > 0 ->
380 let bs = gensym "bs" in
382 let $lid:bs$ = $fexpr$ in
383 if Bitmatch.bitstring_length $lid:bs$ = $flen$ then
384 Bitmatch.construct_bitstring $lid:buffer$ $lid:bs$
386 raise (Bitmatch.Construct_failure
387 ("length of bitstring does not match declaration",
389 $int:loc_line$, $int:loc_char$))
392 (* Bitstring, constant length -1, means variable length bitstring
395 | Bitstring, Some (-1) ->
396 <:expr< Bitmatch.construct_bitstring $lid:buffer$ $fexpr$ >>
398 (* Bitstring, constant length = 0 is probably an error, and so is
401 | Bitstring, Some _ ->
404 "length of bitstring must be > 0 or the special value -1")
406 (* Bitstring, non-constant length.
407 * We check at runtime that the length is > 0 and matches
408 * the declared length.
411 let bslen = gensym "bslen" in
412 let bs = gensym "bs" in
414 let $lid:bslen$ = $flen$ in
415 if $lid:bslen$ > 0 then (
416 let $lid:bs$ = $fexpr$ in
417 if Bitmatch.bitstring_length $lid:bs$ = $lid:bslen$ then
418 Bitmatch.construct_bitstring $lid:buffer$ $lid:bs$
420 raise (Bitmatch.Construct_failure
421 ("length of bitstring does not match declaration",
423 $int:loc_line$, $int:loc_char$))
425 raise (Bitmatch.Construct_failure
426 ("length of bitstring must be > 0",
428 $int:loc_line$, $int:loc_char$))
433 (* Create the final bitstring. Start by creating an empty buffer
434 * and then evaluate each expression above in turn which will
435 * append some more to the bitstring buffer. Finally extract
438 * XXX We almost have enough information to be able to guess
439 * a good initial size for the buffer.
443 | [] -> <:expr< [] >>
444 | h::t -> List.fold_left (fun h t -> <:expr< $h$; $t$ >>) h t in
448 let $lid:buffer$ = Bitmatch.Buffer.create () in
450 Bitmatch.Buffer.contents $lid:buffer$
456 Bitmatch.Construct_failure ("value out of range",
458 $int:loc_line$, $int:loc_char$) in
464 (* Generate the code for a bitmatch statement. '_loc' is the
465 * location, 'bs' is the bitstring parameter, 'cases' are
466 * the list of cases to test against.
468 let output_bitmatch _loc bs cases =
469 let data = gensym "data" and off = gensym "off" and len = gensym "len" in
470 let result = gensym "result" in
472 (* This generates the field extraction code for each
473 * field a single case. Each field must be wider than
474 * the minimum permitted for the type and there must be
475 * enough remaining data in the bitstring to satisfy it.
476 * As we go through the fields, symbols 'data', 'off' and 'len'
477 * track our position and remaining length in the bitstring.
479 * The whole thing is a lot of nested 'if' statements. Code
480 * is generated from the inner-most (last) field outwards.
482 let rec output_field_extraction inner = function
485 let {fpatt=fpatt; fpc={flen=flen; endian=endian; signed=signed;
489 (* Is flen an integer constant? If so, what is it? This
490 * is very simple-minded and only detects simple constants.
494 | <:expr< $int:i$ >> -> Some (int_of_string i)
497 let name_of_int_extract_const = function
498 (* XXX As an enhancement we should allow a 64-bit-only
499 * mode which lets us use 'int' up to 63 bits and won't
500 * compile on 32-bit platforms.
502 (* XXX The meaning of signed/unsigned breaks down at
503 * 31, 32, 63 and 64 bits.
505 | (1, _, _) -> "extract_bit"
506 | ((2|3|4|5|6|7|8), _, false) -> "extract_char_unsigned"
507 | ((2|3|4|5|6|7|8), _, true) -> "extract_char_signed"
508 | (i, BigEndian, false) when i <= 31 -> "extract_int_be_unsigned"
509 | (i, BigEndian, true) when i <= 31 -> "extract_int_be_signed"
510 | (i, LittleEndian, false) when i <= 31 -> "extract_int_le_unsigned"
511 | (i, LittleEndian, true) when i <= 31 -> "extract_int_le_signed"
512 | (i, NativeEndian, false) when i <= 31 -> "extract_int_ne_unsigned"
513 | (i, NativeEndian, true) when i <= 31 -> "extract_int_ne_signed"
514 | (32, BigEndian, false) -> "extract_int32_be_unsigned"
515 | (32, BigEndian, true) -> "extract_int32_be_signed"
516 | (32, LittleEndian, false) -> "extract_int32_le_unsigned"
517 | (32, LittleEndian, true) -> "extract_int32_le_signed"
518 | (32, NativeEndian, false) -> "extract_int32_ne_unsigned"
519 | (32, NativeEndian, true) -> "extract_int32_ne_signed"
520 | (_, BigEndian, false) -> "extract_int64_be_unsigned"
521 | (_, BigEndian, true) -> "extract_int64_be_signed"
522 | (_, LittleEndian, false) -> "extract_int64_le_unsigned"
523 | (_, LittleEndian, true) -> "extract_int64_le_signed"
524 | (_, NativeEndian, false) -> "extract_int64_ne_unsigned"
525 | (_, NativeEndian, true) -> "extract_int64_ne_signed"
527 let name_of_int_extract = function
528 (* XXX As an enhancement we should allow users to
529 * specify that a field length can fit into a char/int/int32
530 * (of course, this would have to be checked at runtime).
532 | (BigEndian, false) -> "extract_int64_be_unsigned"
533 | (BigEndian, true) -> "extract_int64_be_signed"
534 | (LittleEndian, false) -> "extract_int64_le_unsigned"
535 | (LittleEndian, true) -> "extract_int64_le_signed"
536 | (NativeEndian, false) -> "extract_int64_ne_unsigned"
537 | (NativeEndian, true) -> "extract_int64_ne_signed"
541 match t, flen_is_const with
542 (* Common case: int field, constant flen *)
543 | Int, Some i when i > 0 && i <= 64 ->
544 let extract_func = name_of_int_extract_const (i,endian,signed) in
545 let v = gensym "val" in
547 if $lid:len$ >= $flen$ then (
548 let $lid:v$, $lid:off$, $lid:len$ =
549 Bitmatch.$lid:extract_func$ $lid:data$ $lid:off$ $lid:len$
551 match $lid:v$ with $fpatt$ when true -> $inner$ | _ -> ()
556 Loc.raise _loc (Failure "length of int field must be [1..64]")
558 (* Int field, non-const flen. We have to test the range of
559 * the field at runtime. If outside the range it's a no-match
563 let extract_func = name_of_int_extract (endian,signed) in
564 let v = gensym "val" in
566 if $flen$ >= 1 && $flen$ <= 64 && $flen$ <= $lid:len$ then (
567 let $lid:v$, $lid:off$, $lid:len$ =
568 Bitmatch.$lid:extract_func$ $lid:data$ $lid:off$ $lid:len$
570 match $lid:v$ with $fpatt$ when true -> $inner$ | _ -> ()
574 (* String, constant flen > 0. *)
575 | String, Some i when i > 0 && i land 7 = 0 ->
576 let bs = gensym "bs" in
578 if $lid:len$ >= $flen$ then (
579 let $lid:bs$, $lid:off$, $lid:len$ =
580 Bitmatch.extract_bitstring $lid:data$ $lid:off$ $lid:len$
582 match Bitmatch.string_of_bitstring $lid:bs$ with
583 | $fpatt$ when true -> $inner$
588 (* String, constant flen = -1, means consume all the
591 | String, Some i when i = -1 ->
592 let bs = gensym "bs" in
594 let $lid:bs$, $lid:off$, $lid:len$ =
595 Bitmatch.extract_remainder $lid:data$ $lid:off$ $lid:len$ in
596 match Bitmatch.string_of_bitstring $lid:bs$ with
597 | $fpatt$ when true -> $inner$
602 Loc.raise _loc (Failure "length of string must be > 0 and a multiple of 8, or the special value -1")
604 (* String field, non-const flen. We check the flen is > 0
605 * and a multiple of 8 (-1 is not allowed here), at runtime.
608 let bs = gensym "bs" in
610 if $flen$ >= 0 && $flen$ <= $lid:len$
611 && $flen$ land 7 = 0 then (
612 let $lid:bs$, $lid:off$, $lid:len$ =
613 Bitmatch.extract_bitstring
614 $lid:data$ $lid:off$ $lid:len$ $flen$ in
615 match Bitmatch.string_of_bitstring $lid:bs$ with
616 | $fpatt$ when true -> $inner$
621 (* Bitstring, constant flen >= 0.
622 * At the moment all we can do is assign the bitstring to an
625 | Bitstring, Some i when i >= 0 ->
628 | <:patt< $lid:ident$ >> -> ident
629 | <:patt< _ >> -> "_"
632 (Failure "cannot compare a bitstring to a constant") in
634 if $lid:len$ >= $flen$ then (
635 let $lid:ident$, $lid:off$, $lid:len$ =
636 Bitmatch.extract_bitstring $lid:data$ $lid:off$ $lid:len$
642 (* Bitstring, constant flen = -1, means consume all the
645 | Bitstring, Some i when i = -1 ->
648 | <:patt< $lid:ident$ >> -> ident
651 (Failure "cannot compare a bitstring to a constant") in
653 let $lid:ident$, $lid:off$, $lid:len$ =
654 Bitmatch.extract_remainder $lid:data$ $lid:off$ $lid:len$ in
658 | Bitstring, Some _ ->
659 Loc.raise _loc (Failure "length of bitstring must be >= 0 or the special value -1")
661 (* Bitstring field, non-const flen. We check the flen is >= 0
662 * (-1 is not allowed here) at runtime.
667 | <:patt< $lid:ident$ >> -> ident
670 (Failure "cannot compare a bitstring to a constant") in
672 if $flen$ >= 0 && $flen$ <= $lid:len$ then (
673 let $lid:ident$, $lid:off$, $lid:len$ =
674 Bitmatch.extract_bitstring $lid:data$ $lid:off$ $lid:len$
681 (* Emit extra debugging code. *)
683 if not debug then expr else (
684 let field = string_of_patt_field field in
687 if !Bitmatch.debug then (
688 Printf.eprintf "PA_BITMATCH: TEST:\n";
689 Printf.eprintf " %s\n" $str:field$;
690 Printf.eprintf " off %d len %d\n%!" $lid:off$ $lid:len$;
691 (*Bitmatch.hexdump_bitstring stderr
692 ($lid:data$,$lid:off$,$lid:len$);*)
698 output_field_extraction expr fields
701 (* Convert each case in the match. *)
702 let cases = List.map (
703 fun (fields, bind, whenclause, code) ->
704 let inner = <:expr< $lid:result$ := Some ($code$); raise Exit >> in
706 match whenclause with
708 <:expr< if $whenclause$ then $inner$ >>
714 let $lid:name$ = ($lid:data$, $lid:off$, $lid:len$) in
718 output_field_extraction inner (List.rev fields)
721 (* Join them into a single expression.
723 * Don't do it with a normal fold_right because that leaves
724 * 'raise Exit; ()' at the end which causes a compiler warning.
725 * Hence a bit of complexity here.
727 * Note that the number of cases is always >= 1 so List.hd is safe.
729 let cases = List.rev cases in
731 List.fold_left (fun base case -> <:expr< $case$ ; $base$ >>)
732 (List.hd cases) (List.tl cases) in
734 (* The final code just wraps the list of cases in a
735 * try/with construct so that each case is tried in
736 * turn until one case matches (that case sets 'result'
737 * and raises 'Exit' to leave the whole statement).
738 * If result isn't set by the end then we will raise
739 * Match_failure with the location of the bitmatch
740 * statement in the original code.
742 let loc_fname = Loc.file_name _loc in
743 let loc_line = string_of_int (Loc.start_line _loc) in
744 let loc_char = string_of_int (Loc.start_off _loc - Loc.start_bol _loc) in
747 let ($lid:data$, $lid:off$, $lid:len$) = $bs$ in
748 let $lid:result$ = ref None in
752 match ! $lid:result$ with
754 | None -> raise (Match_failure ($str:loc_fname$,
755 $int:loc_line$, $int:loc_char$))
762 [ LIST0 [ q = LIDENT -> q ] SEP "," ]
765 (* Field used in the bitmatch operator (a pattern). *)
767 [ fpatt = patt; ":"; len = expr LEVEL "top";
768 qs = OPT [ ":"; qs = qualifiers -> qs ] ->
769 parse_patt_field _loc fpatt len qs
773 (* Case inside bitmatch operator. *)
776 fields = LIST0 patt_field SEP ";";
778 bind = OPT [ "as"; name = LIDENT -> name ];
779 whenclause = OPT [ "when"; e = expr -> e ]; "->";
781 (fields, bind, whenclause, code)
785 (* Field used in the BITSTRING constructor (an expression). *)
787 [ fexpr = expr LEVEL "top"; ":"; len = expr LEVEL "top";
788 qs = OPT [ ":"; qs = qualifiers -> qs ] ->
789 parse_constr_field _loc fexpr len qs
793 (* 'bitmatch' expressions. *)
796 bs = expr; "with"; OPT "|";
797 cases = LIST1 match_case SEP "|" ->
798 output_bitmatch _loc bs cases
802 | [ "BITSTRING"; "{";
803 fields = LIST0 constr_field SEP ";";
805 output_constructor _loc fields