xref: /Universal-ctags/misc/optlib2c (revision af10eb0b814559bac24ecb5abaaaffd981226601)
1#!/usr/bin/env perl
2#
3# optlib2c - a tool translating ctags option file to C
4#
5# Copyright (C) 2016 Masatake YAMATO
6# Copyright (C) 2016 Red Hat Inc.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20#
21
22use strict;
23use warnings;
24#use Data::Dumper;
25#print Dumper(X);
26
27sub show_help {
28    print<<EOF;
29Usage:
30	$0 --help
31	$0 FILE.ctags > FILE.c
32EOF
33}
34
35
36########################################################################
37#
38# PARSE
39#
40########################################################################
41my $options =
42    [
43     [ qr/^--options=(.*)/, sub {
44	   parse_optlib ($1, $_[0]);
45	   return 1;
46       } ],
47     [ qr/^--__selector-(.*)=(.*)/, sub {
48	 die "Don't use --__selector-<LANG>= option before defining the language"
49	     if (! defined $_[0]->{'langdef'});
50	 $_[0]->{'selector'} = $2;
51	 return 1;
52       } ],
53     #
54     # TODO: We should not assume the order of flags.
55     #
56     [ qr/^--langdef=([^\{]+)(((\{base=([^\{]+)\})(\{(dedicated|shared|bidirectional)\})?)?)(\{_autoFQTag\})?/, sub {
57	   die "LANG is already defined as $_[0]->{'langdef'}: $1"
58	       if (defined $_[0]->{'langdef'});
59	   die "Don't use \"all\" as a language name. It is reserved word."
60	       if ($1 eq "all");
61
62	   $_[0]->{'langdef'} = $1;
63	   $_[0]->{'base'} = $5 if defined $5;
64	   $_[0]->{'direction'} = $7 if defined $7;
65	   $_[0]->{'autoFQTag'} = (defined $8)? 1: 0;
66
67	   die "Don't use a character as a language name other than alphanumeric, # and +: "
68	       . $_[0]->{'langdef'} unless ($_[0]->{'langdef'} =~ /^[a-zA-Z0-9#+]+$/);
69	   return 1;
70       } ],
71     [ qr/^--kinddef-(.*)=([^,]),([^,]+),([^\{]+)(\{_refonly\})?/, sub {
72	 die "Don't use --kinddef-<LANG>=+ option before defining the language"
73	     if (! defined $_[0]->{'langdef'});
74	 die "Adding a kind is allowed only to the language specified with --langdef: $1"
75	     unless ($_[0]->{'langdef'} eq $1);
76
77	 my $letter = $2;
78	 my $name = $3;
79	 my $desc = $4;
80	 my $refonly = 0;
81	 $refonly = 1 if defined $5;
82
83	 die "'F' as a kind letter is reserved for file kind."
84	     if ($letter eq 'F');
85	 die "unacceptable character is used for kind letter: $letter"
86	     unless ($letter =~ /^[a-zA-EG-Z]$/);
87
88	 die "'file' as a kind name is reserved"
89	     if ($name eq 'file');
90	 die "The initial letter of kind name must be alphabetic character: $name"
91	     unless (substr ($name, 0, 1) =~ /[a-zA-Z]/);
92	 die "Letters used in kind name other than initial letter must be alphanumeric characters: $name"
93	     unless (substr ($name, 1) =~ /^[a-zA-Z0-9]+$/);
94
95	 push @{$_[0]->{'kinddefs'}}, { enabled => 1, letter => $letter, name => $name, desc => $desc,
96					refonly => $refonly, roles => [], seps => [] };
97	 return 1;
98       } ],
99     [ qr/^--_extradef-(.*)=([^,]+),([^\{]+)/, sub {
100	 die "Don't use --_extradef-<LANG>=+ option before defining the language"
101	     if (! defined $_[0]->{'langdef'});
102	 die "Adding an extra is allowed only to the language specified with --langdef: $1"
103	     unless ($_[0]->{'langdef'} eq $1);
104
105	 my $name = $2;
106	 my $desc = $3;
107
108	 die "unacceptable character is used for extra name: $name"
109	     unless ($name =~ /^[a-zA-Z0-9]+$/);
110
111	 push @{$_[0]->{'extradefs'}}, { name => $name, desc => $desc };
112	 return 1;
113       } ],
114     [ qr/^--_fielddef-(.*)=([^,]+),([^\{]+)/, sub {
115	 die "Don't use --_fielddef-<LANG>=+ option before defining the language"
116	     if (! defined $_[0]->{'langdef'});
117	 die "Adding a field is allowed only to the language specified with --langdef: $1"
118	     unless ($_[0]->{'langdef'} eq $1);
119
120	 my $name = $2;
121	 my $desc = $3;
122	 die "unacceptable character is used for field name: $name"
123	     unless ($name =~ /^[a-zA-Z]+$/);
124
125	 push @{$_[0]->{'fielddefs'}}, { name => $name, desc => $desc };
126	 return 1;
127       } ],
128     [ qr/^--_roledef-([^.]*)\.(?:([a-zA-Z])|\{([a-zA-Z][a-zA-Z0-9]*)\})=([a-zA-Z0-9]+),([^\{]+)/, sub {
129	   die "Don't use --_roledef-<LANG>.<KNID>=+ option before defining the language"
130	     if (! defined $_[0]->{'langdef'});
131	   die "Adding a field is allowed only to the language specified with --langdef: $1"
132	     unless ($_[0]->{'langdef'} eq $1);
133
134	   my $kind_found = 0;
135	   for (@{$_[0]->{'kinddefs'}}) {
136	       if ((defined $2 && $_->{'letter'} eq $2)
137		   || (defined $3 && $_->{'name'} eq $3)) {
138		   my $role = { name => $4, desc => $5, owner => $_ };
139		   push @{$_->{'roles'}}, $role;
140		   $kind_found = 1;
141		   last;
142	       }
143	   }
144	   die "no such kind, \"$2\" where role \"$3\" is attached to" if (! $kind_found);
145	   return 1;
146       } ],
147     [ qr/^--languages=-(.*)/, sub {
148	   die "Don't use --languages=- option before defining the language"
149	       if (! defined $_[0]->{'langdef'});
150	   die "Only language specified with --langdef can be disabled: $1"
151	       unless ($_[0]->{'langdef'} eq $1);
152	   $_[0]->{'disabled'} = 1;
153	   return 1;
154       } ],
155     [ qr/^--language=(.*)/, sub {
156	   die "--languages can be used only for disabling a language defined with --langdef: $1";
157       } ],
158     [ qr/^--map-([^=]*)=\+(.*)/, sub {
159	   die "Don't use --map-<LANG>=+ option before defining the language"
160	       if (! defined $_[0]->{'langdef'});
161	   die "Adding a map is allowed only to the language specified with --langdef: $1"
162	       unless ($_[0]->{'langdef'} eq $1);
163	   my $spec = $2;
164	   if ($spec =~ /\((.*)\)/) {
165	       push @{$_[0]->{'patterns'}}, $1;
166	   } elsif ($spec =~ /\.(.*)/) {
167	       push @{$_[0]->{'extensions'}}, $1;
168	   } else {
169	       die "Unexpected notation is used in the argument for --map-$1= option";
170	   }
171	   return 1;
172       } ],
173     [ qr/^--map-([^=]*)=[^+].*/, sub {
174	   die "map manipulation other than the appending(--map-<LANG>=+...) is not supported";
175       } ],
176     [ qr /^--alias-([^=]*)=\+(.*)/, sub {
177	   die "Don't use --alias-<LANG>=+ option before defining the language"
178	       if (! defined $_[0]->{'langdef'});
179	   die "Adding an alias is allowed only to the language specified with --langdef: $1"
180	       unless ($_[0]->{'langdef'} eq $1);
181	   push @{$_[0]->{'aliases'}}, $2;
182	   return 1;
183       } ],
184     [ qr/^--alias-([^=]*)=[^+].*/, sub {
185	   die "alias manipulation other than the appending(--alias-<LANG>=+...) is not supported";
186       } ],
187     [ qr/^--regex-([^=]*)=(.*)/, sub {
188	   die "Don't use --regex-<LANG>= option before defining the language"
189	       if (! defined $_[0]->{'langdef'});
190	   die "Adding a regex is allowed only to the language specified with --langdef: $1"
191	       unless ($_[0]->{'langdef'} eq $1);
192	   return parse_regex ($2, $_[0], 0);
193       } ],
194     [ qr/^--mline-regex-([^=]*)=(.*)/, sub {
195	   die "Don't use --mline-regex-<LANG>= option before defining the language"
196	       if (! defined $_[0]->{'langdef'});
197	   die "Adding a multiline regex is allowed only to the language specified with --langdef: $1"
198	       unless ($_[0]->{'langdef'} eq $1);
199	   return parse_regex ($2, $_[0], 1);
200       } ],
201     [ qr/^--kinds-([^=]*)=-(.*)/, sub {
202	   die "Don't use --kinds-<LANG>= option before defining the language"
203	       if (! defined $_[0]->{'langdef'});
204	   parse_kinds ($2, $_[0]);
205	   return 1;
206       } ],
207     [ qr/^--kinds-([^=]*)=(.*)/, sub {
208	   die "--kinds-<LANG>= can be used only for disabling a kind: $1";
209       } ],
210     [ qr/^--extras-([^=]*)=([-+])\{(.+)\}$/, sub {
211	   die "Don't use --extras-<LANG>= option before defining the language"
212	       if (! defined $_[0]->{'langdef'});
213	   die "Enabling/disabling an extra is allowed only to the language specified with --langdef: $1"
214	       unless ($_[0]->{'langdef'} eq $1);
215	   die "Specifing multiple extras in one --extras-... is not handled: {$3}"
216	       if ( index ($3, '{') != -1 );
217	   parse_extras ($2, $3, $_[0]);
218	   return 1;
219       } ],
220     [ qr/^--extras-([^=]*)=\{/, sub {
221	 die "--extras-<LANG>= can be used only for enabling or disabling an extra: $1";
222       } ],
223     [ qr/^--extras-([^=]*)=(.)\{/, sub {
224	 die "Unknown flag($2) is passed to --extras-<LANG>= option";
225       } ],
226     [ qr/^--fields-([^=]*)=([-+])\{(.+)\}$/, sub {
227	   die "Don't use --fields-<LANG>= option before defining the language"
228	       if (! defined $_[0]->{'langdef'});
229	   die "Enabling/disabling a field is allowed only to the language specified with --langdef: $1"
230	       unless ($_[0]->{'langdef'} eq $1);
231	   die "Specifing multiple fields in one --fields-... is not handled: {$3}"
232	       if ( index ($3, '{') != -1 );
233	   parse_fields ($2, $3, $_[0]);
234	   return 1;
235       } ],
236     [ qr/^--fields-([^=]*)=\{/, sub {
237	 die "--fields-<LANG>= can be used only for enabling or disabling a field: $1";
238       } ],
239     [ qr/^--fields-([^=]*)=(.)\{/, sub {
240	 die "Unknown flag($2) is passed to --fields-<LANG>= option";
241       } ],
242     [ qr/^--langmap=.*/, sub {
243	 die "Use --map-<LANG> option instead of --langmap";
244       } ],
245     [ qr/^--_tabledef-([^=]*)=(.*)/, sub {
246	 die "Don't use --_tabledef-<LANG>= option before defining the language"
247	   if (! defined $_[0]->{'langdef'});
248	 die "Adding a table is allowed only to the language specified with --langdef: $1"
249	   unless ($_[0]->{'langdef'} eq $1);
250	 $_[0]->{'tabledefs'}->{$2} = [];
251	 push @{$_[0]->{'tablenames'}}, "$2";
252	 return 1;
253       } ],
254     [ qr/^--_mtable-regex-([^=]*)=([^\/]+)(\/.*)/, sub {
255	 die "Don't use --_mtable-regex-<LANG>= option before defining the language"
256	   if (! defined $_[0]->{'langdef'});
257	 die "Adding a multitable regex is allowed only to the language specified with --langdef: $1"
258	   unless ($_[0]->{'langdef'} eq $1);
259	 return parse_regex ($3, $_[0], 1, $2);
260       } ],
261     [ qr/^--_mtable-extend-([^=]*)=(.*)\+(.*)/, sub {
262	 die "Don't use --_mline-extend-<LANG>= option before defining the language"
263	   if (! defined $_[0]->{'langdef'});
264	 die "Extending a multitable regex is allowed only to the language specified with --langdef: $1"
265	   unless ($_[0]->{'langdef'} eq $1);
266	 extend_table($_[0], $2, $3);
267	 return 1;
268     } ],
269     [ qr/^--_(prelude|sequel)-([^=]*)=(\{\{)$/, sub {
270	 die "Don't use --_mtable-regex-<LANG>= option before defining the language"
271	   if (! defined $_[0]->{'langdef'});
272
273	 my $hook = $_[0]->{$1};
274	 my $slot = @$hook;
275	 push @$hook, $3;
276	 return sub {
277	   $hook->[$slot] = $hook->[$slot] . $_[0];
278	 }
279       }],
280     [ qr/^--_(prelude|sequel)-([^=]*)=(\{\{.*\}\})$/, sub {
281	 die "Don't use --_mtable-regex-<LANG>= option before defining the language"
282	   if (! defined $_[0]->{'langdef'});
283
284	 my $hook = $_[0]->{$1};
285	 my $slot = @$hook;
286	 push @$hook, $3;
287	 return 1;
288       }],
289     [ qr/^--_scopesep-([^=]*)=([^,]?)\/([^:]+):(.+)/, sub {
290	 die "Don't use --_scopesep-<LANG>= option before defining the language"
291	   if (! defined $_[0]->{'langdef'});
292	 die "Specifying a scope separator is allowed only to the language specified with --langdef: $1"
293	   unless ($_[0]->{'langdef'} eq $1);
294	 scopesep($_[0], $2, $3, $4);
295	 return 1;
296       } ],
297     [ qr/^-.*/, sub {
298	 die "Unhandled option: \"$&\"";
299       } ],
300     [ qr/.*/, sub {
301	 die "Unhandled argument: \"$&\"";
302       } ],
303    ];
304
305sub parse_line {
306    my ($line, $opts) = @_;
307    my $r = 0;
308
309    for (@{$options}) {
310	my ($pat, $action) = @{$_};
311	if ($line =~ $pat) {
312	    $r = $action -> ($opts);
313	    last;
314	}
315    }
316    $r;
317}
318
319sub gather_chars {
320    my $input = shift;
321    my $output = "";
322
323    my $escape = 0;
324    my $c;
325
326    # See scanSeparators() of lregex.c.
327    while (defined ($c = shift @{$input})) {
328	if ($escape) {
329	    if ($c eq '/') {
330		$output = $output . $c;
331	    } elsif ($c eq 't') {
332		$output = $output . '\\' . 't';
333	    } elsif ($c eq 'n') {
334		$output = $output . '\\' . 'n';
335	    } elsif ($c eq '\\') {
336		$output = $output . '\\\\' . '\\\\';
337	    } else {
338		$output = $output . '\\\\' . $c;
339	    }
340	    $escape = 0;
341	} elsif ($c eq '"') {
342	    $output = $output . '\\' . $c;
343	} elsif ($c eq '\\') {
344	    $escape = 1;
345	} elsif ($c eq '/') {
346	    last;
347	} else {
348	    $output = $output . $c;
349	}
350    }
351
352    return ($output, (defined $c)? $c: "");
353}
354
355sub parse_regex {
356    my ($regspec, $opts, $mline, $table) = @_;
357
358    my @chars = split //, $regspec;
359
360    # TODO:
361    #	ctags allows using a char other than '/'
362    #
363    if (! ($chars[0] eq '/')) {
364	if (!$mline) {
365	    die "--regex-<LANG>= option is not started from /: $regspec";
366	} else {
367	    die "--mline-regex-<LANG>= option is not started from /: $regspec";
368	}
369    }
370
371    shift @chars;
372    my $last_char;
373
374    my $regex;
375    ($regex, $last_char) = gather_chars (\@chars);
376    if (! ($last_char eq '/')) {
377	if (!$mline) {
378	    die "The pattern in --regex-<LANG>= option is not ended with /: $regspec";
379	} else {
380	    die "The pattern in --mline-regex-<LANG>= option is not ended with /: $regspec";
381	}
382    }
383
384    my $name;
385    ($name, $last_char) = gather_chars (\@chars);
386    if (! ($last_char eq '/')) {
387	die "Wrong kind/flags syntax: $regspec";
388    }
389
390    my $tmp;
391    ($tmp, $last_char) = gather_chars (\@chars);
392    my $kind = "";
393    my $flags;
394
395    if ( (! @chars) && (! ($last_char eq '/'))) {
396	$flags = $tmp;
397
398    } else {
399	$kind = $tmp;
400    }
401
402    if ($last_char eq '/') {
403	($flags, $last_char) = gather_chars (\@chars);
404    }
405
406    my $optscript = '';
407    if ($flags =~ /(.*)\{\{$/) {
408	$flags = $1;
409	$optscript = "{{\n";
410    }
411
412    my $entry;
413    if (defined $table) {
414
415      if (! (substr ($regex, 0, 1) eq '^')) {
416	$regex = '^' . $regex;
417      }
418      $entry = { 'regex' => $regex,
419		 'name'  => $name,
420		 'kind'  => $kind,
421		 'flags' => $flags,
422		 'mline' => $mline,
423		 'optscript' => $optscript,
424	       };
425      push @{$opts->{'tabledefs'}->{"$table"}}, $entry;
426    } else {
427      $entry = { 'regex' => $regex,
428		 'name'  => $name,
429		 'kind'  => $kind,
430		 'flags' => $flags,
431		 'mline' => $mline,
432		 'optscript' => $optscript,
433	       };
434      push @{$opts->{'regexs'}}, $entry;
435    }
436
437    if ($flags =~ '{scope=' || $optscript) {
438	$opts->{'useCork'} = 1;
439    }
440
441    return $optscript
442	? sub {
443	    $entry->{'optscript'} = $entry->{'optscript'} . $_[0];
444	}
445	: 1;
446}
447
448sub extend_table {
449  my ($opts, $dist, $src) = @_;
450
451  for (@{$opts->{'tabledefs'}->{$src}}) {
452    push @{$opts->{'tabledefs'}->{$dist}}, $_;
453  }
454}
455
456sub parse_kinds {
457    my ($kinds, $opts) = @_;
458    for my $letter (split //, $kinds) {
459	for (@{$opts->{'kinddefs'}}) {
460	    if ($_->{'letter'} eq $letter) {
461		$_->{'enabled'} = 0;
462	    }
463	}
464    }
465}
466
467sub parse_optlib {
468    my ($optlib, $opts) = @_;
469
470    open(my $optlibfh, "<", $optlib)
471	or die "cannot open the optlib file: \"$optlib\"";
472
473    my $receiver = 0;
474    while (<$optlibfh>) {
475	chomp;
476
477	if (/^\}\}$/ && $receiver && ($receiver != 1)) {
478	    $receiver->('}}');
479	    $receiver = 1;
480	}
481	elsif ($receiver && $receiver != 1) {
482	    my $str = escape_as_cstr ($_);
483	    $receiver->($str);
484	    $receiver->("\n");
485	} elsif ( /^[[:blank:]]*#.*/ ) {
486	    next;
487	} else {
488	    s/^\s*//;
489	    next if ( /^\s*$/ );
490	    $receiver = parse_line ($_, $opts);
491	    if (! $receiver) {
492		return undef;
493	    }
494	}
495    }
496    return $opts;
497}
498
499sub parse_extras {
500    my ($flag, $extra, $opts) = @_;
501
502    $flag = ( $flag eq '+' )?1: 0;
503
504    # TODO: Hash table should be used for manage 'extradefs'.
505    for (@{$opts->{'extradefs'}}) {
506	if ($_->{'name'} eq $extra)
507	{
508	    $_->{'enabled'} = $flag;
509	}
510    }
511}
512
513sub parse_fields {
514    my ($flag, $field, $opts) = @_;
515
516    $flag = ( $flag eq '+' )?1: 0;
517
518    # TODO: Hash table should be used for manage 'fielddefs'.
519    for (@{$opts->{'fielddefs'}}) {
520	if ($_->{'name'} eq $field)
521	{
522	    $_->{'enabled'} = $flag;
523	}
524    }
525}
526
527sub scopesep {
528    my ($opts, $parent_kletter, $kletter, $sep) = @_;
529
530    if ($kletter eq '*') {
531	if ($parent_kletter eq '*') {
532	    $opts->{'defaultScopeSeparator'} = $sep;
533	} elsif (!$parent_kletter) {
534	    $opts->{'defaultRootScopeSeparator'} = $sep;
535	} else {
536	    die "Unhandled kind letter during parsing --_scopsesep option: $parent_kletter";
537	}
538    } elsif ($kletter =~ /[a-zA-Z]/) {
539	my $kind;
540	for (@{$opts->{'kinddefs'}}) {
541	    if ($_->{'letter'} eq $kletter) {
542		$kind = $_;
543		last;
544	    }
545	}
546	die "Unknown kind letter in --_scopsesep option: $kletter"
547	  unless defined $kind;
548
549	if ($parent_kletter eq '*') {
550	    push @{$kind->{'seps'}}, { parentKindIndex => 'KIND_WILDCARD_INDEX',
551				       sep => $sep };
552	} elsif (!$parent_kletter) {
553	    push @{$kind->{'seps'}}, { parentKindIndex => 'KIND_GHOST_INDEX',
554				       sep => $sep };
555	} elsif ($parent_kletter =~ /[a-zA-Z]/) {
556	    my $parent_kind;
557	    for (@{$opts->{'kinddefs'}}) {
558		if ($_->{'letter'} == $parent_kletter) {
559		    $parent_kind = $_;
560		    last;
561		}
562	    }
563	    die "Unknown kind letter in --_scopsesep option: $parent_kletter"
564	      unless defined $parent_kind;
565	    push @{$kind->{'seps'}}, { parentKindIndex => 'K_' . (uc $parent_kind->{'name'}),
566				       sep => $sep };
567	}
568    } else {
569	die "Unhandled kind letter during parsing --_scopsesep option: $kletter";
570    }
571
572    $opts->{'hasSepSpeicifer'} = 1;
573}
574
575
576########################################################################
577#
578# EMIT
579#
580########################################################################
581
582sub emit_header {
583    my ($optlib, $opts) = @_;
584
585    print <<EOF;
586/*
587 * Generated by $0 from ${optlib}, Don't edit this manually.
588 */
589#include "general.h"
590#include "parse.h"
591#include "routines.h"
592#include "field.h"
593#include "xtag.h"
594EOF
595
596    if (defined $opts->{'base'}) {
597	print <<EOF;
598#include "subparser.h"
599EOF
600    }
601    if (defined $opts->{'selector'}) {
602	print <<EOF;
603#include "selectors.h"
604EOF
605    }
606    print <<EOF;
607
608
609EOF
610}
611
612sub emit_initializer {
613    my $opts = shift;
614    my $may_unused = $opts->{'tablenames'} ? "": " CTAGS_ATTR_UNUSED";
615
616    print <<EOF;
617static void initialize$opts->{'Clangdef'}Parser (const langType language$may_unused)
618{
619EOF
620    for (@{$opts->{'prelude'}}) {
621	my $prelude = "";
622	for my $frag (split /\n/, $_) {
623	    $prelude = $prelude . "\n\t\t\"" . $frag . ($frag =~ /.*\}\}$/
624							? '"'
625							: '\n"');
626	}
627	print <<EOF;
628	addLanguageOptscriptToHook (language, SCRIPT_HOOK_PRELUDE,$prelude);
629EOF
630    }
631    for (@{$opts->{'sequel'}}) {
632	my $sequel = "";
633	for my $frag (split /\n/, $_) {
634	    $sequel = $sequel . "\n\t\t\"" . $frag . ($frag =~ /.*\}\}$/
635							? '"'
636							: '\n"');
637	}
638	print <<EOF;
639	addLanguageOptscriptToHook (language, SCRIPT_HOOK_SEQUEL,$sequel);
640EOF
641    }
642    if ($opts->{'tablenames'}) {
643      print "\n";
644
645      for (@{$opts->{'tablenames'}}) {
646	print <<EOF;
647	addLanguageRegexTable (language, "$_");
648EOF
649      }
650
651      print "\n";
652
653      for my $table (@{$opts->{'tablenames'}}) {
654	for (@{$opts->{'tabledefs'}->{"$table"}}) {
655	  my $optscript = "";
656	  if ($_-> {'optscript'}) {
657	    for my $frag (split /\n/, $_-> {'optscript'}) {
658	      $optscript = $optscript . "\n\t\t\"" . $frag . ($frag =~ /.*\}\}$/
659							      ? '"'
660							      : '\n"');
661	    }
662	  }
663	  print <<EOF;
664	addLanguageTagMultiTableRegex (language, "$table",
665	                               "$_->{'regex'}",
666	                               "$_->{'name'}", "$_->{'kind'}", "$_->{'flags'}"$optscript, NULL);
667EOF
668	}
669      }
670    }
671    print <<EOF;
672}
673
674EOF
675    0;
676}
677
678sub emit_list {
679    my ($opts, $key) = @_;
680
681    print <<EOF;
682	static const char *const $key [] = {
683EOF
684    for (@{$opts->{$key}}) {
685	print <<EOF;
686		\"$_\",
687EOF
688    }
689
690    print <<EOF;
691		NULL
692	};
693
694EOF
695}
696
697sub emit_aliases {
698    emit_list $_[0], "aliases";
699}
700
701sub emit_extensions {
702    emit_list $_[0], "extensions";
703}
704
705sub emit_patterns {
706    emit_list $_[0], "patterns";
707}
708
709# TODO: handling '\'
710sub escape_as_cstr {
711    my $input = shift;
712    my $output = "";
713
714    for my $c (split //, $input) {
715	if ($c eq '"') {
716	    $output = $output . '\\' . '"';
717	} elsif ($c eq '\\') {
718	    $output = $output . '\\' . '\\';
719	} else {
720	    $output = $output . $c;
721	}
722    }
723
724    return $output;
725}
726
727sub emit_roledefs {
728    my $opts = shift;
729
730
731    for (@{$opts->{'kinddefs'}}) {
732	next unless @{$_->{'roles'}};
733	my $Kind = capitalize($_->{'name'});
734	print <<EOF;
735	static roleDefinition $opts->{'Clangdef'}${Kind}RoleTable [] = {
736EOF
737	for (@{$_->{'roles'}}) {
738	    my $desc = escape_as_cstr $_->{'desc'};
739	    print <<EOF;
740		{ true, "$_->{'name'}", "$desc" },
741EOF
742	}
743
744	print <<EOF;
745	};
746EOF
747    }
748    print <<EOF;
749EOF
750}
751
752sub emit_kinddef_enums {
753    my $opts = shift;
754
755    return if (! @{$opts->{'kinddefs'}});
756
757    print <<EOF;
758typedef enum {
759EOF
760    for (@{$opts->{'kinddefs'}}) {
761	my $e = uc($_->{'name'});
762	print <<EOF;
763	K_$e,
764EOF
765    }
766    print <<EOF;
767} $opts->{'Clangdef'}Kind;
768
769
770EOF
771}
772
773sub emit_scopeseps {
774    my $opts = shift;
775
776    return if (! @{$opts->{'kinddefs'}});
777
778    for (@{$opts->{'kinddefs'}}) {
779	my $seps = $_->{'seps'};
780	if (@{$seps}) {
781	    my $Kind = capitalize ($_->{'name'});
782	    print <<EOF;
783	static scopeSeparator $opts->{'Clangdef'}${Kind}Separators [] = {
784EOF
785	    for (@{$seps}) {
786	    print <<EOF;
787		{ $_->{'parentKindIndex'}, "$_->{'sep'}" },
788EOF
789	    }
790	    print <<EOF;
791	};
792
793EOF
794	}
795    }
796}
797
798sub emit_kinddefs {
799    my $opts = shift;
800
801    return if (! @{$opts->{'kinddefs'}});
802
803    print <<EOF;
804	static kindDefinition $opts->{'Clangdef'}KindTable [] = {
805EOF
806    for (@{$opts->{'kinddefs'}}) {
807      my $enabled = $_->{"enabled"}? "true": "false";
808      print <<EOF;
809		{
810EOF
811      my $desc = escape_as_cstr $_->{'desc'};
812      print <<EOF;
813		  $enabled, \'$_->{'letter'}\', "$_->{'name'}", "$desc",
814EOF
815      if ($_->{'refonly'}) {
816	  print <<EOF;
817		  .referenceOnly = true,
818EOF
819      }
820      my $Kind = capitalize($_->{'name'});
821      if (@{$_->{'roles'}}) {
822	  print <<EOF;
823		  ATTACH_ROLES($opts->{'Clangdef'}${Kind}RoleTable),
824EOF
825      }
826      if (@{$_->{'seps'}}) {
827	  print <<EOF;
828		  ATTACH_SEPARATORS($opts->{'Clangdef'}${Kind}Separators),
829EOF
830      }
831      print <<EOF;
832		},
833EOF
834    }
835    print <<EOF;
836	};
837EOF
838}
839
840sub emit_regexs {
841    my $opts = shift;
842
843    return if (! @{$opts->{'regexs'}});
844
845    print <<EOF;
846	static tagRegexTable $opts->{'Clangdef'}TagRegexTable [] = {
847EOF
848    for (@{$opts->{'regexs'}}) {
849	my $flags;
850	if ($_-> {'flags'}) {
851	    $flags = '"' . $_-> {'flags'} . '"';
852	    if ($_-> {'optscript'}) {
853		for my $frag (split /\n/, $_-> {'optscript'}) {
854		    $flags = $flags . "\n\t\t\"" . $frag . ($frag =~ /.*\}\}$/
855							    ? '"'
856							    : '\n"');
857		}
858	    }
859	} else {
860	    if ($_-> {'optscript'}) {
861		$flags = '""';
862		for my $frag (split /\n/, $_-> {'optscript'}) {
863		    $flags = $flags . "\n\t\t\"" . $frag . ($frag =~ /.*\}\}$/
864							    ? '"'
865							    : '\n"');
866		}
867	    } else {
868		$flags = 'NULL';
869	    }
870	}
871	my $mline = $_-> {'mline'}? "true": "false";
872	print <<EOF;
873		{"$_->{'regex'}", "$_->{'name'}",
874		"$_->{'kind'}", $flags, NULL, $mline},
875EOF
876    }
877    print <<EOF;
878	};
879
880EOF
881}
882
883sub emit_dependencies {
884    my $opts = shift;
885
886    return if (! defined $opts->{'base'});
887
888    my $direction = "SUBPARSER_BASE_RUNS_SUB";
889
890    if (defined $opts->{'direction'})
891    {
892	if ($opts->{'direction'} eq 'shared')
893	{
894	    $direction = "SUBPARSER_BASE_RUNS_SUB";
895	} elsif ($opts->{'direction'} eq 'dedicated')
896	{
897	    $direction = "SUBPARSER_SUB_RUNS_BASE";
898	} elsif ($opts->{'direction'} eq 'bidirectional')
899	{
900	    $direction = "SUBPARSER_BI_DIRECTION";
901	}
902    }
903
904    print <<EOF;
905	static subparser $opts->{'Clangdef'}Subparser = {
906		.direction = $direction,
907	};
908	static parserDependency $opts->{'Clangdef'}Dependencies [] = {
909		[0] = { DEPTYPE_SUBPARSER, "$opts->{'base'}", &$opts->{'Clangdef'}Subparser },
910	};
911EOF
912
913}
914
915sub emit_xtags {
916    my $opts = shift;
917
918    return if (! @{$opts->{'extradefs'}});
919
920    print <<EOF;
921	static xtagDefinition $opts->{'Clangdef'}XtagTable [] = {
922EOF
923    for (@{$opts->{'extradefs'}}) {
924      my $enabled = $_->{"enabled"}? "true": "false";
925      my $desc = escape_as_cstr $_->{'desc'};
926      print <<EOF;
927		{
928		  .enabled     = $enabled,
929		  .name        = "$_->{'name'}",
930		  .description = "$desc",
931		},
932EOF
933    }
934    print <<EOF;
935	};
936EOF
937}
938
939sub emit_fields {
940    my $opts = shift;
941
942    return if (! @{$opts->{'fielddefs'}});
943
944    print <<EOF;
945	static fieldDefinition $opts->{'Clangdef'}FieldTable [] = {
946EOF
947    for (@{$opts->{'fielddefs'}}) {
948      my $enabled = $_->{"enabled"}? "true": "false";
949      my $desc = escape_as_cstr $_->{'desc'};
950      print <<EOF;
951		{
952		  .enabled     = $enabled,
953		  .name        = "$_->{'name'}",
954		  .description = "$desc",
955		},
956EOF
957    }
958    print <<EOF;
959	};
960EOF
961}
962
963sub emit_selector {
964    my $opts = shift;
965
966    if (defined $opts->{'selector'}) {
967	print <<EOF;
968	static selectLanguage selectors[] = { $opts->{'selector'}, NULL };
969EOF
970    }
971}
972
973sub emit_fields_initialization {
974    my $opts = shift;
975    my $enabled = $opts->{"disabled"} ? "false": "true";
976    my $method;
977    my $sep;
978
979    $method  = "METHOD_NOT_CRAFTED";
980    if (@{$opts->{"regexs"}} || $opts->{'tablenames'}) {
981	$method = "${method}|METHOD_REGEX";
982    }
983
984    print <<EOF;
985	def->enabled       = ${enabled};
986	def->extensions    = extensions;
987	def->patterns      = patterns;
988	def->aliases       = aliases;
989EOF
990    if (defined $opts->{'selector'}) {
991	print <<EOF;
992	def->selectLanguage= selectors;
993EOF
994    }
995print <<EOF;
996	def->method        = ${method};
997EOF
998
999    if ($opts->{'useCork'}) {
1000	print <<EOF;
1001	def->useCork       = CORK_QUEUE;
1002EOF
1003    }
1004    if (@{$opts->{'kinddefs'}}) {
1005	print <<EOF;
1006	def->kindTable     = $opts->{'Clangdef'}KindTable;
1007	def->kindCount     = ARRAY_SIZE($opts->{'Clangdef'}KindTable);
1008EOF
1009    }
1010
1011    if (@{$opts->{'fielddefs'}}) {
1012	print <<EOF;
1013	def->fieldTable    = $opts->{'Clangdef'}FieldTable;
1014	def->fieldCount    = ARRAY_SIZE($opts->{'Clangdef'}FieldTable);
1015EOF
1016    }
1017
1018    if (@{$opts->{'extradefs'}}) {
1019	print <<EOF;
1020	def->xtagTable     = $opts->{'Clangdef'}XtagTable;
1021	def->xtagCount     = ARRAY_SIZE($opts->{'Clangdef'}XtagTable);
1022EOF
1023    }
1024
1025    if (@{$opts->{'regexs'}}) {
1026	print <<EOF;
1027	def->tagRegexTable = $opts->{'Clangdef'}TagRegexTable;
1028	def->tagRegexCount = ARRAY_SIZE($opts->{'Clangdef'}TagRegexTable);
1029EOF
1030    }
1031
1032    if (defined $opts->{'base'}) {
1033	print <<EOF;
1034	def->dependencies    = $opts->{'Clangdef'}Dependencies;
1035	def->dependencyCount = ARRAY_SIZE($opts->{'Clangdef'}Dependencies);
1036EOF
1037    }
1038
1039    if ($opts->{'autoFQTag'}) {
1040	print <<EOF;
1041	def->requestAutomaticFQTag = true;
1042EOF
1043    }
1044
1045    if ($opts->{'defaultScopeSeparator'}) {
1046	$sep = "$opts->{'defaultScopeSeparator'}";
1047	$sep = escape_as_cstr "$sep";
1048	print <<EOF;
1049	def->defaultScopeSeparator = "$sep";
1050EOF
1051    }
1052    if ($opts->{'defaultRootScopeSeparator'}) {
1053	$sep = $opts->{'defaultRootScopeSeparator'};
1054	$sep = escape_as_cstr "$sep";
1055	print <<EOF;
1056	def->defaultRootScopeSeparator = "$sep";
1057EOF
1058    }
1059    print <<EOF;
1060	def->initialize    = initialize$opts->{'Clangdef'}Parser;
1061
1062EOF
1063}
1064
1065sub emit {
1066    my ($optlib, $opts) = @_;
1067
1068    emit_header ($optlib, $opts);
1069
1070    if ($opts->{'hasSepSpeicifer'}) {
1071	emit_kinddef_enums   $opts;
1072    };
1073    emit_initializer $opts;
1074
1075    print <<EOF;
1076extern parserDefinition* $opts->{'Clangdef'}Parser (void)
1077{
1078EOF
1079
1080    emit_extensions      $opts;
1081    emit_aliases         $opts;
1082    emit_patterns        $opts;
1083    emit_roledefs        $opts;
1084    emit_scopeseps       $opts;
1085    emit_kinddefs        $opts;
1086    emit_fields          $opts;
1087    emit_xtags           $opts;
1088    emit_regexs          $opts;
1089    emit_dependencies    $opts;
1090    emit_selector         $opts;
1091
1092    print "\n";
1093
1094    print <<EOF;
1095	parserDefinition* const def = parserNew ("$opts->{'langdef'}");
1096
1097EOF
1098
1099    emit_fields_initialization $opts;
1100
1101    print <<EOF;
1102	return def;
1103}
1104EOF
1105}
1106
1107########################################################################
1108#
1109# REARRANGE
1110#
1111########################################################################
1112
1113sub capitalize {
1114    my ($str) = $_[0];
1115    my $c = substr ($str, 0, 1);
1116
1117    $c =~ tr/a-z/A-Z/;
1118
1119    return $c . substr($str, 1);
1120}
1121
1122sub rearrange {
1123    my ($opts) = @_;
1124    my $langdef = $opts -> {'langdef'};
1125    $opts -> {'Clangdef'} = capitalize ($langdef);
1126}
1127
1128
1129########################################################################
1130#
1131# MAIN
1132#
1133########################################################################
1134
1135sub main {
1136    my $optlib;
1137    my %opts = (langdef  => undef,
1138		Clangdef => undef,
1139		disabled => 0,
1140		patterns => [],
1141		extensions => [],
1142		aliases => [],
1143		regexs => [# { regex => "", name => "", kind => "", flags => "", mline => 1|0, optscript => "" },
1144			  ],
1145		kinddefs => [# { letter => '', name => "", desc => "",
1146			     #   seps => [ {parentKindIndex => "", sep => "" } ]
1147			     # },
1148			    ],
1149		extradefs => [ # { name => "", desc => "", enabled => 1|0 },
1150			     ],
1151		fielddefs => [ # { name => "", desc => "", enabled => 1|0 },
1152			      ],
1153		base => undef,
1154		tableNames => [ # ""
1155			       ],
1156		tabledefs => { # "" => [{ regex => "", name => "", kind => "", flags => "" }...],
1157			     },
1158		useCork   => 0,
1159		defaultScopeSeparator => undef,
1160		defaultRootScopeSeparator => undef,
1161		hasSepSpeicifer => 0,
1162		prelude => [ # ""
1163			   ],
1164		sequel =>  [ # ""
1165			   ],
1166		selector => undef,
1167	       );
1168
1169    for (@_) {
1170	if ( ($_ eq '-h') || ($_ eq '--help') ) {
1171	    show_help;
1172	    exit 0;
1173	} elsif ( /^-.*/ ) {
1174	    die "unrecongnized option: $_";
1175	} else {
1176	    if ($optlib) {
1177		die "Too man input files: @_";
1178	    }
1179	    $optlib=$_;
1180	}
1181    }
1182
1183    if (! $optlib) {
1184	die "No optlib file name is given";
1185    }
1186
1187    if (! parse_optlib ($optlib, \%opts)) {
1188	exit 1;
1189    }
1190
1191    rearrange (\%opts);
1192
1193    if (! emit ($optlib, \%opts) ) {
1194	exit 1;
1195    }
1196    0;
1197}
1198
1199main @ARGV;
1200
1201# optlib2c ends here.
1202