[BACK]Return to libspec.pm CVS log [TXT][DIR] Up to [Development] / projects / ogl-sample / main / tools / libspec

File: [Development] / projects / ogl-sample / main / tools / libspec / libspec.pm (download)

Revision 1.2, Wed Apr 5 06:43:49 2000 UTC (17 years, 6 months ago) by ljp
Branch: MAIN
Changes since 1.1: +1 -1 lines

Change license version to 1.1.

#
# License Applicability. Except to the extent portions of this file are
# made subject to an alternative license as permitted in the SGI Free
# Software License B, Version 1.1 (the "License"), the contents of this
# file are subject only to the provisions of the License. You may not use
# this file except in compliance with the License. You may obtain a copy
# of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
# Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
# 
# http://oss.sgi.com/projects/FreeB
# 
# Note that, as provided in the License, the Software is distributed on an
# "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
# DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
# CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
# PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
# 
# Original Code. The Original Code is: OpenGL Sample Implementation,
# Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
# Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
# Copyright in any portions created by third parties is as indicated
# elsewhere herein. All Rights Reserved.
# 
# Additional Notice Provisions: The application programming interfaces
# established by SGI in conjunction with the Original Code are The
# OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
# April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
# 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
# Window System(R) (Version 1.3), released October 19, 1998. This software
# was created using the OpenGL(R) version 1.2.1 Sample Implementation
# published by SGI, but has not been independently verified as being
# compliant with the OpenGL(R) version 1.2.1 Specification.
#

#
# NAME:
#	libspec.pm -- perl script to parse .spec files
#
# DESCRIPTION:
#	This perl script parsest the .spec format for each function
#	interface, generates values into global veriables, and then calls
#	the user's main() function to generate output.  The main() function
#	grabs the appropriate info from the global variables.
#	The user can also specify an initialize() and finalize() function.
#
# AUTHORS:
#	Initial authors of awk script:
#		Dave Ciemiewicz (ciemo)
#		Gary Tarolli (gt)
#		David Mott (mott) - for gldebug
#		tweeked by numerous people in the OpenGL teams
#	Ported to perl:
#		George Kyriazis (kyriazis)
#


#
# $Date$ $Revision$
# $Header: //depot/main/tools/libspec/libspec.pm#5 $
#

use Getopt::Long;
use File::Basename;

use libspeccutils;

$validDirections{"in"} = 1;
$validDirections{"out"} = 1;
$validDirections{"inout"} = 1;

$validTransferTypes{"value"} = 1;
$validTransferTypes{"reference"} = 1;
$validTransferTypes{"array"} = 1;

#
# strip comments
#
sub stripcomments {
    my ($in) = @_;

    $in =~ s/^[ \t]*\$//;	# remove empty lines
    $in =~ s/[ \t]*\#.*$//;	# comments at end of line (& comment lines)

    if ($in =~ /^$/) { return ""; }

    $in;
}

#
# canonicalize pathname, according to the operating system's liking
#
sub canonicalizePathname {
    my ($filename) = @_;
    my ($path) = $ENV{PATH};

    if ($path =~ m/\\/) {
	# OS is windows (replace slashes with backslashes)
	$filename =~ s/\//\\/;
    } else {
	# OS is unix (replaces backslashes with slashes)
	$filename =~ s/\\/\//;
    }

    return $filename;
}

#
# output copyright message
#
sub outputCopyright {
    my ($type) = @_;
    my ($message);

    $message = 'THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT';

    open(COPYRIGHT, $optionvars{"copyright"}) ||
	die("Cannot open copyright file\n");
    print "\n";
    if ($type eq "c") {
    	print "/*\n";
	while(<COPYRIGHT>) {
	    print "** ", $_;
	}
	print "*/\n\n";
	print "/* ", $message, " */\n\n";
    }
    if ($type eq "sh") {
    	print "#\n";
    	while(<COPYRIGHT>) {
	    print "# ", $_;
	}
    	print "#\n\n";
	print "# ", $message, "\n\n";
    }
    close(COPYRIGHT);
}


#
# print a read error message fo the file/linenumber specified
#
sub readError {
    my ($arg1, $arg2, $arg3) = @_;

    if ($arg2 eq "") {
	error($arg1);
	return;
    }
    if ($arg3 eq "") {
	error("file " . $arg1 . ": " . $arg2);
	return;
    }
    error("file " . $arg1 . ", line " . $arg2 . ": " . $arg3);
}

#
# print an error message
#
sub error {
    my ($err) = @_;

    print STDERR $err . "\n";
}

#
# reset parseing states to false
#
# The parsing routines will indicate errors by setting one of those flags
#
sub _resetParseStates {
    $parseErrorOccured = 0;
    $parsedPrototype = 0;
    $parsedReturn = 0;
}

#
# returns true if argument is a constant (decimal) value and not an identifier
#
sub isConstant {
    my ($string) = @_;

    if ($string =~ /^[0-9]+$/) {
	return 1;
    } else {
	return 0;
    }
}

##############################################################################
#
#  Wire Functions
#
##############################################################################


##############################################################################
#
#  NAME
#	_readWireFile()
#
#  DESCRIPTION
#	Create the wire map by reading the specified wire file.  The
#	format of the file is simple: 2 columns, left column is the
#	DeclaredType of the parameter,the right column is the wire
#	format for the parameter.
#
##############################################################################
sub _readWireFile {
    my ($WireFile, $typeMapFileWithSlashes);
    my (@f);

    $typeMapFileWithSlashes = $typeMapFile;
    $typeMapFileWithSlashes =~ s/\\/\//g;
    $WireFile = dirname($typeMapFileWithSlashes) . "/spec2wire.map";
    $WireFile = canonicalizePathname($WireFile);

    open(WIREFILE, $WireFile) || die("Cannot open wire file\n");
    while(<WIREFILE>) {
    	chop;
	$_ = stripcomments $_;
	@f = split /[ \t]+/, $_;

	if ($#f < 1) { next; }

	$wiretype{$f[0]} = $f[1];
    }
    close (WIREFILE);
}



##############################################################################
#
#  Type Map Functions
#
##############################################################################


##############################################################################
#
#  NAME
#       _readTypeMap(typeMapFile)
#
#  DESCRIPTION
#       Create the type map by reading the specified typeMapFile
#       invoking the _makeTypeMap() function.
#
#       The type map uses a contextual wildcard character "*" for
#       short-hand description of type maps.  The expansion of "*"
#       is as follows:
#
#               Field                   "*" Wildcard Expansion
#               ====================    ====================================
#               unmapped-direction      All directions (in, out, in/out)
#               unmapped-transfer-type  All transfer-types (array, reference,
#                                       ... and value)
#               mapped-direction        Corresponding unmapped-direction
#               mapped-transfer-type    Corresponding unmapped-transfer-type
#
#  TYPE MAP FILE BNF
#       <type-map-file> ::= <type-map-line>*
#       <type-map-line> :== [<type-map>] [<comment>]
#       <type-map> :== <unmapped-type> "," <mapped-type>
#       <meta-unmapped-type> :== <unmapped-declared-type> ","
#                       <meta-unmapped-direction> ","
#                       <meta-unmapped-transfer-type>
#       <unmapped-declared-type> :== <type-name>
#       <meta-unmapped-direction> :== <direction> | "*"
#       <meta-unmapped-transfer-type> :== <transfer-type> | "*"
#       <meta-mapped-type> :== <meta-mapped-declared-type> ","
#                       <meta-mapped-direction> ","
#                       <meta-mapped-transfer-type>
#       <meta-mapped-declared-type> :== <type-name>
#       <meta-mapped-direction> :== <direction> | "*"
#       <meta-mapped-transfer-type> :== <transfer-type> | "*"
#       <direction> :== "in" | "out" | "in/out"
#       <transfer-type> :== "array" | "reference" | "value"
#
##############################################################################
sub _readTypeMap {
    my ($typeMapFile, $d, $t) = @_;

    if ($typeMapFile eq "") {
	readError("libspec.pl", "no typeMapFile specified in _readTypeMap\n");
	exit(1);
    }

    open(TYPEMAPFILE, $typeMapFile) || die("Cannot open typemapfile\n");
    while (<TYPEMAPFILE>) {
	chop;
	$_ = stripcomments($_);
	@f = split /[ \t]*,[ \t]*/, $_;
	if ($f[1] eq "*") {
	    foreach $d (keys %validDirections) {
		if ($f[2] eq "*") {
		    foreach $t (keys %validTransferTypes) {
			_makeTypeMap($f[0], $d, $t, $f[3], $f[4], $f[5]);
		    }
		} elsif (defined $validTransferTypes{$f[2]}) {
		    _makeTypeMap($f[0], $d, $f[2], $f[3], $f[4], $f[5]);
		} else {
		    readError($typeMapFile, "unknown transfer type: " . $f[4]);
		}
	    }
	} elsif (defined $validDirections{$f[1]}) {
	    if ($f[2] eq "*") {
		foreach $t (keys %validTransferTypes) {
		    _makeTypeMap($f[0], $f[1], $t, $f[3], $f[4], $f[5]);
		}
	    } elsif (defined $validTransferTypes{$f[2]}) {
		_makeTypeMap($f[0], $f[1], $f[2], $f[3], $f[4], $f[5]);
	    } else {
		readError($typeMapFile, "unknown transfer type: " . $f[4]);
	    }
	} else {
	    readError($typeMapFile, "unknown transfer type: " . $f[4]);
	}
    }
    close TYPEMAPFILE;
}

sub _makeTypeMap {
    my ($type, $direction, $transfer, $maptype, $mapdir, $maptrans) = @_;

    $typeMap{$type, $direction, $transfer} = 1;

    if ($maptype eq "*") {
	$typeMapDeclaredType{$type, $direction, $transfer} = $type;
    } else {
	$typeMapDeclaredType{$type, $direction, $transfer} = $maptype;
    }

    if ($mapdir eq "*") {
	$typeMapDirection{$type, $direction, $transfer} = $direction;
    } elsif (defined $validDirections{$mapdir}) {
	$typeMapDirection{$type, $direction, $transfer} = $mapdir;
    } else {
	error("unknown direction: " . $mapdir);
    }

    if ($maptrans eq "*") {
	$typeMapTransferType{$type, $direction, $transfer} = $transfer;
    } elsif (defined $validTransferTypes{$maptrans}) {
	$typeMapTransferType{$type, $direction, $transfer} = $maptrans;
    } else {
	error("unknown transfer type: " . $maptrans);
    }
}

##############################################################################
#
#  NAME
#       _parseValidPropsDeclaration()
#
#  SPEC FILE BNF
#       <valid-props-declaration> ::= <prop-name> ":" <prop-values>
#       <prop-values> ::= (<blank> <prop-value>)*
#
#  SETS
#       validPropList[propname] -- Set of valid props
#                               -- ... [] is a comma separated list of
#                               -- ... valid prop values
#       validPropListValues[propname,value]
#                               -- Set of valid (propname,values)
#
#       _parseErrorOccured      -- Flag indicating error in parsing
#
##############################################################################
sub _parseValidPropsDeclaration {
    my ($i);

    $f[0] =~ s/://;	# get rid of color from list name
    for ($i = 1; $i <= $#f; $i++) {
	$validPropList{$f[0]} = 1;
	$validPropListValues{$f[0], $f[$i]} = 1;
    }
}

##############################################################################
#
#  NAME
#       _parseFunctionPrototype()
#
#  SPEC FILE BNF
#       <function-prototype> :== <function-name> "(" <param-list> ")"
#       <param-list> :== [<param-name> ["," <param-name>]* ]
#
#  SETS
#       functionName            -- Name of function currently being processed
#       paramCount              -- The number of parameters for function
#       paramName[i]            -- The name of the i-th parameter
#       paramList[name]         -- Set of parameter names for function
#                               -- ... [] is the parameter index
#
#
##############################################################################
sub _parseFunctionPrototype {
    my (@protoFields) = split /[(), \t]+/, $_;
    my ($i);

    $functionName = $protoFields[0];
    $paramCount = 0;
    %paramList = ();
    for ($i = 1; $i <= $#protoFields; $i++) {
	$paramCount++;
	$paramName{$paramCount} = $protoFields[$i];
	$paramList{$protoFields[$i]} = $paramCount;
    }
}

##############################################################################
#
#  NAME
#       _parseProp()
#
#  SPEC FILE BNF
#       <prop-line> ::= <prop-indent> <prop-name> <meta-prop-value>*
#       <meta-prop-value> :== <blank> ["!"] ("all" | <prop-value>)
#
#  SETS
#       propList[propname]      -- Set of properties for this function
#                               -- ... [] is a comma separated list of
#                               -- ... prop values
#       _parseErrorOccured      -- Flag indicating error in parsing
#
##############################################################################
sub _parseProp {
    my ($i);

    if (defined $validPropList{$f[0]}) {	# if it is a valid prop
	$propList{$f[0]} = "";			# create it for this function
	for ($i = 1; $i <= $#f; $i++) {
	    if (!_parseMetaPropValue($f[0], $f[$i])) {
		specError("unknown prop " . $f[0] . " value: " . $f[$i]);
		$parseErrorOccured = 1;
	    }
	}
    } else {
	specError("unknown prop " . $f[0] . "\n");
	$parseErrorOccured = 1;
    }
}

##############################################################################
#
#  NAME
#       _parseReturnType()
#
#  DESCRIPTION
#       Parse the return type.  A "void" type implies a procedure or
#       subroutine.  Return type is the <unmapped-declared-type>.
#       The <unmapped-direction> is implied to be "out" and the
#       <unmapped-transfer-type> is implied to be "value".
#
#  SPEC FILE BNF
#       <unmapped-return-type> :== <unmapped-declared-type>
#
#  SETS
#       returnUnmappedType      -- Return type from spec file
#       returnType              -- Converted or mapped return type
#       _parseErrorOccured      -- Flag indicating error in parsing
#
##############################################################################
sub _parseReturnType {
    $returnUnmappedType = $f[1];
    if (!defined $typeMap{$returnUnmappedType, "out", "value"}) {
	# if returnUnmappedType is not a valid type ...
	specError("return type " . $returnUnmappedType .
		  " is not defined in " . $typeMapFile);
	$parseErrorOccured = 1;
    } else {
	$returnType = $typeMapDeclaredType{$returnUnmappedType,"out","value"};
    }
}

##############################################################################
#
#  NAME
#       _parseParamPropBody()
#
#  SPEC FILE BNF
#       <param-prop-body> :== <param-name> <blank> <unmapped-param-type>
#                               [<blank> <length-descriptor>]
#                               [<param-options-list>]
#       <unmapped-param-type> :== <unmapped-declared-type> <blank>
#                               <unmapped-direction> <blank>
#                               <unmapped-transfer-type>
#       <unmapped-declared-type> :== <type-name>
#       <unmapped-direction> :== "in" | "out" | "in/out"
#       <unmapped-transfer-type> :== "array" | "reference" | "value"
#       <length-descriptor> :== "[" <index-expr> ["," <index-expr> ]* "]"
#       <index-expr> :== <integer> | <param-name> |
#                       ["("] <index-expr> <operator> <index-expr> [")"]
#       <operator> :== "+" | "-" | "*" | "/"
#       <param-options-list> :== (<blank> <list-prop-value>)*
#
#  SETS
#       paramUnmappedDeclaredType[name]
#                               -- Parameter type from spec file
#       paramUnmappedDirection[name]
#                               -- Parameter direction [in,out,in/out] from
#                               -- ... spec file
#       paramUnmappedTransferType[name]
#                               -- Parameter transfer type
#                               -- ... [value, reference, array] from spec fil
#       paramDeclaredType[name] -- Parameter type from spec file
#       paramDirection[name]    -- Parameter direction [in,out,in/out] from
#                               -- ... spec file
#       paramTransferType[name] -- Parameter transfer type
#                               -- ... [value, reference, array] from spec fil
#       paramDimensions[name]   -- The number dimensions of the array/length
#                               -- ... 0 if no array/length descriptor
#       paramSubscripts[name,i] -- The size/value of the i-th dimension
#       paramOptions[name]      -- Space separated list of parameter options
#       paramOptions[name,option]
#                               -- Set of options for parameter
#       _parseErrorOccured      -- Flag indicating error in parsing
#
##############################################################################
sub _parseParamPropBody {
    my ($name);
    my ($tmpLengthDesc);
    my (@tmpSubscripts);
    my ($i);
    my (@operand);
    my ($operands);
    my ($processedLengthDesc);

    $name = $f[1];
    if (!defined $paramList{$name}) {
	specError("param " . $name . " not in function prototype for "
		  . $functionName);
	$parseErrorOccured = 1;
    }

    if (!defined $typeMap{$f[2], $f[3], $f[4]}) {	# is the type mapped?
	specError($f[2] . " " . $f[3] . " " . $f[4] . " is not defined in "
		  . $typeMapFile);
	$parseErrorOccured = 1;
    }

    $paramUnmappedDeclaredType{$name} = $f[2];
    $paramDirection{$name} = $f[3];
    $paramTransferType{$name} = $f[4];

    $paramDeclaredType{$name} = $typeMapDeclaredType{$f[2], $f[3], $f[4]};
    $paramDirection{$name} = $typeMapDirection{$f[2], $f[3], $f[4]};
    $paramTransferType{$name} = $typeMapTransferType{$f[2], $f[3], $f[4]};

    #
    # if the parameter has a length/array descriptor
    # break it up into the following variables:
    #
    $paramDimensions{$name} = 0;
    if ($f[5] =~ /^\[.*\]$/) {
	$tmpLengthDesc = $f[5];
	$tmpLengthDesc =~ s/[\[\]]//g;
	@tmpSubscripts = split /,/, $tmpLengthDesc;
	$paramDimensions{$name} = @tmpSubscripts;	# number of elements
	for ($i=0; $i < $paramDimensions{$name}; $i++) {
	    if ($tmpSubscripts[$i] ne "") {
		$paramSubscripts{$name, $i+1} = $tmpSubscripts[$i];
		if ($tmpSubscripts[$i] =~ /MAX/) {
		    $paramSubscripts{$name, $i+1} = $paramSubscripts{$name, $i+1} . "," . $tmpSubscripts[$i+1];
		    $i++;
		    $paramDimensions{$name}--;
		}
	    } else {
		specError("subscript " . $i . " is unspecified");
		$parseErrorOccured = 1;
	    }
	}

	#
	# check operands in subscripts to make sure they are legal
	#
	$operands = $f[5];
	@operand = split /[-+\\\/*(),\[\]]/, $operands;

	foreach $i (1 .. $#operand) {
	    # not an arg or a number
	    if (!(defined $paramList{$operand[$i]}) && !isConstant($operand[$i])) {
		if (($operand[$i] ne "MAX") && ($operand[$i] ne "COMPSIZE")) {
		    # allow MAX and COMPSIZE keywords
		    specError("subscript size operand " . $operand[$i]
			      . " is not a parameter");
		    $parseErrorOccured = 1;
		}
	    }
	}
	$processedLengthDesc = 1;
    } else {
	$processedLengthDesc = 0;
    }

    #
    # process any parameter options if there are any
    #
    if ((!$processedLengthDesc) && ($#f > 4)) {
	$i = 5;		# if no len descr, start at field 6
    } elsif ($processedLengthDesc && ($#f > 5)) {
	$i = 6;		# if len descr, start at field 7
    } else {
	$i = $#f + 1;	# no parameter options;
    }

    for (; $i <= $#f; $i++) {
	if (defined $validPropListValues{"param", $f[$i]}) {
	    $paramOptions{$name, $f[$i]} = 1;
	    $paramOptions{$name} = $paramOptions{$name} . $f[$i] . " ";
	} else {
	    specError("unknown prop param value: " . $f[$i]);
	}
    }
}

##############################################################################
#
#  NAME
#       _parseMetaPropValue(name, value)
#
#  DESCRIPTION
#       Add name and value to propList array if name is a valid property
#       If value is equal to all, all of the values from validPropList[]
#       are added to propListValues.  If the value begins with an exclamation
#       point (!), the name,value pair is deleted from the propListValues.
#       This ! negation permits "all but 'value'" short-hand notation.
#
#  SPEC FILE BNF
#       <meta-prop-value> :== <blank> ["!"] ("all" | <prop-value>)
#
#  SETS
#       propList[propname]      -- Set of properties for this function
#                               -- ... [] is a comma separated list of
#                               -- ... prop values
#       propListValues[propname,value]
#                               -- Set of (propname,values) for this function
#
#
##############################################################################
sub _parseMetaPropValue {
    my ($name, $value) = @_;
    my ($i);
    my ($nf);
    my (@values);
    my ($s);

    if ($value =~ /^!/) {	# if negation, remove from list
	$value =~ s/^!//g;	# remove negation mark
	if ($value eq "all") {
	    @values = split /,/, $validPropList{$name};
	    $nf = @values;
	    for ($i = 2; $i <= $nf; $i++) {
		delete $propListValues{$name, $values[i]};
		$s = "," . $values[$i]; 
		$propList{$name} =~ s/$s//g;
	    }
	} elsif (defined $validPropListValues{$name, $value}) {
	    delete $propListValues{$name, $value};
	    $s = "," . $value;
	    $propList{$name} =~ s/$s//g;
	} elsif (defined $validPropListValues{$name,"*"}) {
	    delete $propListValues{$name, $value};
	    $s = "," . $value;
	    $propList{$name} =~ s/$s//g;
	} else {
	    return 0;	# propagate error up one level
	}
    } else {		# if not negation, add to list
	if ($value eq "all") {
	    $propList{$name} = $propList{$name} . $validPropList{$name};
	    @values = split /,/, $validPropList{$name};
	    $nf = @values;
	    for ($i = 2; $i <= $nf; $i++) {
		$propListValues{$name, $values[$i]} = 1;
	    }
	} elsif (defined $validPropListValues{$name, $value}) {
	    $propListValues{$name, $value} = 1;
	    $propList{$name} = $propList{$name} . "," . $value;
	} elsif (defined $validPropListValues{$name, "*"}) {
	    $propListValues{$name, $value} = 1;
	    $propList{$name} = $propList{$name} . "," . $value;
	} else {
	    return 0;		# propagate error up one level
	}
    }
    return 1;
}


#
# check for parsing errors
#
sub _parsingErrors {
    if ($parsedPrototype) {
	if (!$parsedReturn) {
	    specError("return type never parsed for " . $functionName);
	    $parseErrorOccured = 1;
	}
	foreach $i (keys %unparsedRequiredProps) {
	    specError("required prop " . $i . " never parsed for " 
		      . $functionName);
	    $parseErrorOccured = 1;
	}
	return $parseErrorOccured;
    } else {
	return 1;
    }
}

#
# print an error for the spec file being read
#
sub specError {
    readError($specFile, $specline, @_);
}

#
# START HERE
#

# get options
%optionvars = ();
$optionstatus = GetOptions(\%optionvars,
			   "typemap=s",
			   "spec=s",
			   "copyright=s",
			   @options
			   );

$typeMapFile = $optionvars{"typemap"};
$specFile = $optionvars{"spec"};


# Do user's initialization
initialize();

# first check if user initialized everything needed
if ($typeMapFile eq "") {
    readError($libSpecProgram, "no typeMapFile specified\n");
}
if ($specFile eq "") {
    readError($libSpecProgram, "no spec file specified\n");
}


_readTypeMap($typeMapFile);
_resetParseStates();


# now go and read the spec file.
##############################################################################
#
#  SECTION
#       Parser pattern-actions
#
#  DESCRIPTION
#       The following awk pattern-action statements match input lines from
#       the spec file and call the appropriate parsing functions to parse the
#       lines.  If the parsing function is trivial, then it is in-line coded
#
#  SPEC FILE BNF
#       <spec-file> :== <declaration-line>* <function-description>*
#       <declaration-line> :== <required-props-declaration> |
#                               <valid-props-declaration>
#       <function-description> :== <function-prototype> <prop-lines>
#                                  <end-of-function-description>
#
#       <return-prop-line> ::= <prop-indent> <return-prop>
#       <return-prop> ::= "return" <blank> <unmapped-return-type>
#
#       <param-prop-line> ::= <prop-indent> <param-prop>
#       <param-prop> ::= "param" <blank> <param-prop-body>
#
#       <prop-line> ::= <prop-indent> <prop-name> <meta-prop-value>*
#
##############################################################################

open(SPEC, $specFile) || die("libspec.pl: cannot open spec file\n");
$specline = 0;

while(<SPEC>) {
    $specline++;
    chop;
##############################################################################
#
#  Parse comment lines and comments from spec file
#
#  SPEC FILE BNF
#       <comment-line> ::= <comment>
#       <comment> ::= <blank> "#" <text-upto-newline>
#
##############################################################################
    if (/^[ \t]*\#/) { next; }
    s/[ \t]*\#.*$//;	# trim comments

    @f = split /[ \t]+/;
    # special case parsing when beginning with tab
    if (/^\t/) {
	$line = $_;
	$line =~ s/^\t//;
	@f = split /[ \t]+/, $line;
    }
##############################################################################
#
#  Parse return prop
#
#  SPEC FILE BNF
#       <return-prop-line> ::= <prop-indent> <return-prop>
#       <return-prop> ::= "return" <blank> <unmapped-return-type>
#
##############################################################################
    if ($f[0] eq "return") {
	_parseReturnType();
	$parsedReturn = 1;
	next;
    }

##############################################################################
#
#  Parse param prop
#
#  SPEC FILE BNF
#       <param-prop-line> ::= <prop-indent> <param-prop>
#       <param-prop> ::= "param" <blank> <param-prop-body>
#
##############################################################################
    if ($f[0] eq "param") {
	_parseParamPropBody();
	next;
    }

##############################################################################
#
#  Parse prop lines
#
#  SPEC FILE BNF
#       <prop-line> ::= <prop-indent> <prop-name> <meta-prop-value>*
#
##############################################################################
    if (/^\t/) {
	_parseProp();
	delete $unparsedRequiredProps{$f[0]};	# check off required prop
	next;
    }

##############################################################################
#
#  Parse function prototype
#
#  SPEC FILE BNF
#       <function-prototype> :== <function-name> "(" <param-list> ")"
#
##############################################################################
    if (/^[^\t]*\(/) {
	_parseFunctionPrototype();
	$parsedPrototype = 1;

	# setup list to determine which props haven't yet been parsed
	foreach $i (keys %requiredProps) {
	    $unparsedRequiredProps{$i} = 1;
	}
	next;
    }

##############################################################################
#
#  Parse end of function description
#
#  SPEC FILE BNF
#       <end-of-function-description> ::= ( <empty-line> | <end-of-file> )
#
##############################################################################
    if (/^$/) {
	if (!_parsingErrors()) {
	    main();	# user's main routine
	}
	_resetParseStates();
	%propList = ();
	%propListValues = ();
	%paramName = ();
	%paramList = ();
	%paramUnmappedDeclaredType = ();
	%paramUnmappedDirection = ();
	%paramUnmappedTransferType = ();
	%paramDeclaredType = ();
	%paramDirection = ();
	%paramTransferType = ();
	%paramDimenstions = ();
	%paramSubscripts = ();
	%paramOptions = ();

	next;
    }


##############################################################################
#
#  Parse required props declaration
#
#  SPEC FILE BNF
#       <required-props-declaration> ::= "required-props:"<required-prop-list>
#       <required-prop-list> ::= (<blank> <prop-name>)*
#
#  SETS
#       requiredProps[propName] -- Set of required props
#
##############################################################################
    if (/^required-props:/) {
	for ($i=1; $i <= $#f; $i++) {
	    $requiredProps{$f[$i]} = 1;
	}
	next;
    }

##############################################################################
#
#  Parse valid props declarations
#
#  SPEC FILE BNF
#       <valid-props-declaration> ::= <prop-name> ":" <prop-values>
#
##############################################################################
    if (/^[^\t]*:/) {
	_parseValidPropsDeclaration();
	next;
    }

}
close SPEC;

if (!_parsingErrors()) {
    main();	# user's main routine for final spec entry
}

finalize();	# user's finalize routine

# return a true value
1;