Parsing comments again (cpp linemarkers)

[email protected] Thu, 3 Jun 2004 15:54:56 +0200
Newsgroups perl.recdescent
Message-ID <[email protected]>
Hi,

I'm almost finished with the script (the stripped down test case 
is on the bottom of this mail) which parses cpp-preprocessed output.

The pecularity of parsing cpp-output is that if the cpp-processed
file #includes a file, then cpp prints a linemarker line, like:

	# 1 "..\\mmstorageiface\\group\\bld.inf" 1

In my script I keep track of the current working directory and
update it (in the updateCurdir() below) whenever a linemarker
rule (with flags 1 or 2) triggers. The script works fine for
the most of my input files, but there are few that break it.

There are 5 kind of sections started by the "section" terminal.
And the problem comes when the input file #includes another
file, while being in the middle of one section, like here:

	PRJ_MMPFILES
	MMSTORAGEIFACE.MMP
	..\GROUP\PNMSG.MMP
	# 72 "bld.inf" 2
	makefile ..\group\convcolor.mk

The section above is started by the "PRJ_MMPFILES" terminal,
then 2 paths to .mmp files are coming and then (unexpected)
the cpp-linemarker comes before the last path.

What could I do now to solve this problem, please?

I can't just skip the linemarker comments as in P::RD::FAQ.

I also don't want to prepend the linemarker subrule to each
of the 5 section rules, like here:

	prj_mmpfile: /PRJ_MMPFILES\b/i 
		(linemarker | mmpfile[\%::prj_mmpfiles])(s?)

because that would make my script really unreadable and
because I'll have to add an if-statement into each of the
5 actions in order to distinguish if it was triggered by
another linemarker or by the normal section entry.

Any advices please?

Regards
Alex


#!/usr/bin/perl

use strict;
use vars qw($parser $text %prj_platforms %prj_exports 
	%prj_testexports %prj_mmpfiles %prj_testmmpfiles);
use Parse::RecDescent;
# platforms to use when 'DEFAULT' has been specified
use constant DEFPLATS => qw(ARMI ARM4 THUMB WINS WINSCW);
#$RD_WARN  = 1;
#$RD_HINT  = 1;
#$RD_TRACE = 120;
use constant GRAMMAR => q(

inffile: chunk(s) /^\Z/
chunk:	linemarker | 
	prj_platform |
	prj_export |
	prj_testexport | 
	prj_mmpfile |
	prj_testmmpfile |
	<error>

# cpp linemarker, as described in cpp manual "Preprocessor output"
linemarker: '#' <skip: '[ \t]+'> linenum path flag(s?) {
	::updateCurdir($item{path}, $item[-1]);
}
linenum: /\d+/
flag: '1' | '2' | '3' | '4'

prj_platform: /PRJ_PLATFORMS\b/i platform(s?) {
	# go through all specified platforms
	for my $p (@{$item[-1]}) {
		if ($p =~ /^DEFAULT$/i) {
			# store the names of 5 default platforms
			@::prj_platforms{::DEFPLATS} = (1) x 5;
		} else {
			$::prj_platforms{uc $p} = 1;
		}
	}
}
# TODO: make these terminals ignore filenames like TOOLS.mk
platform:
	/ARM4\b/i |
	/ARMI\b/i |
	/DEFAULT\b/i |
	/MCOT\b/i |
	/MCOY\b/i |
	/MEIG\b/i |
	/MHELEN\b/i |
	/MINT\b/i |
	/MISA\b/i |
	/MLNK\b/i |
	/MTEMPLATE\b/i |
	/MWD2\b/i |
	/THUMB\b/i |
	# don't match TOOLS.mk
	/TOOLS(?=(?:\s|\Z))/i |
	/WINC\b/i |
	/WINSCW\b/i |
	/WINS\b/i

# the rules prj_export and prj_testexport below are similar, so they
# just pass a reference to the corresponding hash down to the export rule
prj_export: /PRJ_EXPORTS\b/i export[\%::prj_exports](s?)
prj_testexport: /PRJ_TESTEXPORTS\b/i export[\%::prj_testexports](s?)
# export statements are terminated by newlines, so don't skip newlines
export: path <skip: '[ \t]+'> path(?) {
	::storeExpPath($arg[0], $item[1], $item[-1]->[0]);
}

# the rules prj_mmpfile and prj_testmmpfile below are similar, so they
# just pass a reference to the corresponding hash down to the mmpfile rule
prj_mmpfile: /PRJ_MMPFILES\b/i mmpfile[\%::prj_mmpfiles](s?)
prj_testmmpfile: /PRJ_TESTMMPFILES\b/i mmpfile[\%::prj_testmmpfiles](s?)
mmpfile: makefile(?) path special(?) {
	::storeMmpPath($arg[0], $item[1]->[0], $item{path}, $item[-1]->[0]);
}

path:	/"([^"]*)"/ {
		::unixifyPath($1);
	}
	| ...!section ...!platform ...!linemarker /\S+/ {
		::unixifyPath($item[-1]);
	}
section:
	/PRJ_EXPORTS\b/i |
	/PRJ_MMPFILES\b/i |
	/PRJ_PLATFORMS\b/i |
	/PRJ_TESTEXPORTS\b/i |
	/PRJ_TESTMMPFILES\b/i
makefile:
	/GNUMAKEFILE\b/i |
	/MAKEFILE\b/i |
	/NMAKEFILE\b/i
special:
	/TIDY\b/i |
	/IGNORE\b/i |
	/MANUAL\b/i |
	/SUPPORT\b/i
);

# the actual functions are larger, but I've skipped them here
sub unixifyPath($) { return 1; }
sub updateCurdir($$) { return 1; }
sub storeExpPath($$$) { return 1; }
sub storeMmpPath($$$$) { return 1; }

$parser = Parse::RecDescent->new(GRAMMAR) or die 'Bad grammar';
$text .= $_ while (<DATA>);
defined $parser->inffile($text) or die 'Bad text';

__DATA__
PRJ_PLATFORMS
DEFAULT WINC

PRJ_EXPORTS
..\inc\cmmphonebookstoreextinterface.h	\tcf\cmmphonebookstoreextinterface.h
..\inc\cmmphonebookstoremesshandler.h	\tcf

..\inc\cmmonstoremesshandler.h		MANUAL
..\inc\cmmonstoreextinterface.h		ignore

PRJ_TESTEXPORTS
..\inc\cmmenstoreextinterface.h		\tcf\cmmenstoreextinterface.h
..\inc\cmmenstoremesshandler.h

mmstorageiface.iby	\epoc32\rom\include\mmstorageiface.iby

# 1 "..\\mmstorageiface\\group\\bld.inf" 1

PRJ_MMPFILES
MMSTORAGEIFACE.MMP
..\GROUP\PNMSG.MMP
# 72 "bld.inf" 2
makefile ..\group\convcolor.mk

PRJ_TESTMMPFILES