2
# ============================================================ #
5
# Purpose : a simple pseudo-C-preprocessor #
7
# The C-preprocessor behaves different on different #
8
# systems (e.g. clang, suse) for ARB_GDEmenus. #
9
# That resulted in various failures, #
10
# some detected at compile-time, others at run-time. #
12
# Coded by Ralf Westram (coder@reallysoft.de) in June 2012 #
13
# Institute of Microbiology (Technical University Munich) #
14
# http://www.arb-home.de/ #
16
# ============================================================ #
20
# - comment parsing is error-prone
25
sub parseOneParam(\$) {
33
while ($$code_r =~ /[()[\],\"\']/o) {
34
my ($before,$sep,$after) = ($`,$&,$');
36
if ($before =~ /\\$/o) { goto SHIFT; }
37
if ($inside eq '"' or $inside eq '\'') {
38
if ($sep eq $inside) { goto POP; }
43
return $param.$before;
45
if ($sep eq '\'' or $sep eq '"' or $sep eq '(' or $sep eq '[') {
46
push @instack, $inside;
52
$$code_r = $sep.$after;
53
return $param.$before;
55
if ($inside ne '(') { die "Misplaced ')' in '$$code_r'\n"; }
59
if ($inside ne '[') { die "Misplaced ']' in '$$code_r'\n"; }
61
$inside = pop @instack;
63
$param .= $before.$sep;
67
die "unhandled separator: param='$param'\nbefore='$before'\nsep='$sep'\nafter='$after'\ncode_r='$$code_r'";
77
sub parseMacroParams($\@) {
78
my ($code,$param_r) = @_;
80
if (not $code =~ /^\(/o) { die "Expected '(', seen '$code'"; }
85
if ($code =~ /^\)/o) { $code = $'; last PARAM; }
86
if ($code eq '') { die "Missing or misplaced ')'"; }
88
my $param = parseOneParam($code);
89
push @$param_r, $param;
94
sub apply_define($\@);
95
sub apply_define($\@) {
96
my ($line,$defr) = @_;
99
if ($line =~ /\b$name\b/) {
100
my ($prefix,$suffix) = ($`,$');
101
my $pcount = $$defr[1];
103
return $prefix.$$defr[2].apply_define($suffix,@$defr);
107
$suffix = parseMacroParams($suffix,@param);
109
my $paramCount = scalar(@param);
110
if ($paramCount ne $pcount) {
111
die "Expected $pcount arguments for macro '$name' (found $paramCount)\n";
114
my $expanded = $$defr[$pcount+2];
115
for (my $p=0; $p<$pcount; $p++) {
116
my $search = $$defr[$p+2];
117
my $replace = $param[$p];
118
$expanded =~ s#$search#$replace#g;
121
return $prefix.$expanded.apply_define($suffix,@$defr);
126
my @define = (); # list of defines (applied in order). contains array refs to [ name, pcount, [ pnames...,] content ]
127
my %define = (); # known defines
129
sub apply_defines($) {
131
foreach my $defr (@define) {
132
$line = apply_define($line, @$defr);
139
unshift @define, \@def;
140
$define{$def[0]} = 1;
146
if ($rest =~ /^[A-Z0-9_]+/io) {
147
my ($name,$param) = ($&,$');
149
def_define($name, 0, '');
151
elsif ($param =~ /^\s+/o) {
152
def_define($name, 0, apply_defines($'));
154
elsif ($param =~ /^\(([a-z0-9,_]+)\)\s+/io) {
155
my ($args,$def) = ($1,$');
157
my @args = split /,/,$args;
158
my $count = scalar(@args);
160
my @array = ( $name, $count );
161
foreach (@args) { push @array, $_; }
162
push @array, apply_defines($def);
166
die "invalid macro parameter '$param'";
170
die "invalid define '$rest'\n";
176
if ($rest =~ /^[A-Z0-9_]+/io) {
178
if (exists $define{$name}) {
181
if ($$def_r[0] eq $name) { ; }
184
delete $define{$name};
187
die "'$name' has not been defined";
191
die "invalid undef '$rest'\n";
196
if ($rest =~ /^[A-Z0-9_]+/io) {
198
exists $define{$name};
201
die "invalid ifdef '$rest'\n";
205
my $inMultiLineComment = 0;
207
sub remove_comments($);
208
sub remove_comments($) {
210
if ($inMultiLineComment) {
211
if ($line =~ /\*\//o) {
212
$inMultiLineComment--;
215
if ($inMultiLineComment) {
219
if ($line =~ /^[^'"]*\/\//o) {
222
if ($line =~ /\/\*/o) {
223
$inMultiLineComment++;
224
return remove_comments($');
231
my @include = (); # list of include directories
233
sub include_via_ipath($) {
236
my $rel = $_.'/'.$name;
242
die "Could not find include file '$name'\n";
247
if ($spec =~ /^\"([^\"]+)\"/o) {
249
if (-f $name) { preprocess($name); }
250
else { include_via_ipath($name); }
252
elsif ($spec =~ /^<([^>]+)>/o) {
254
include_via_ipath($name);
256
else { die "no idea how to include '$spec'\n"; }
265
open(my $IN,'<'.$src) || die "can't read '$src' (Reason: $!)";
267
while (defined($line=<$IN>)) {
268
while ($line =~ /\\\n/o) { # concat multilines
270
my $nextLine = <$IN>;
271
if (not defined $nextLine) { die "runaway multiline"; }
272
$line = $body.$nextLine;
276
if ($line =~ /^\s*[#]\s*([^\s]*)\s+/o) {
277
my ($token,$rest) = ($1,$');
279
if ($token eq 'define') { add_define($rest); }
280
elsif ($token eq 'undef') { rm_define($rest); }
281
elsif ($token eq 'include') {
283
eval { include($rest); };
285
if ($@) { die "included from here\n$@"; }
287
elsif ($token eq 'ifdef') {
288
push @skipstack, $skip;
289
$skip = is_defined($rest) ? 0 : 1;
291
elsif ($token eq 'else') {
292
if (scalar(@skipstack)==0) { die "else w/o if\n"; }
295
elsif ($token eq 'endif') {
296
if (scalar(@skipstack)==0) { die "endif w/o if\n"; }
297
$skip = pop @skipstack;
299
else { die "unknown preprocessor token='$token' rest='$rest'\n"; }
303
$line = remove_comments($line);
304
print apply_defines($line);
308
if ($@) { die "$src:$.: $@\n"; }
310
if (scalar(@skipstack)!=0) { die "EOF reached while inside if\n"; }
314
sub addIncludePaths($) {
316
my @paths = split /;/, $pathlist;
317
foreach (@paths) { push @include, $_; }
328
if (defined $src) { die "Multiple sources specified ('$src' and '$_')\n"; }
335
if ($@) { die "$@ (in pp.pl)\n"; }