#! /usr/bin/perl -w
#
# Class name: ParseTree
# Synopsis: Used by headerdoc2html to hold parse trees
# Last Updated: $Date: 2014/03/06 11:20:09 $
#
# Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
# This file contains Original Code and/or Modifications of Original Code
# as defined in and that are subject to the Apple Public Source License
# Version 2.0 (the 'License'). You may not use this file except in
# compliance with the License. Please obtain a copy of the License at
# http://www.opensource.apple.com/apsl/ and read it before using this
# file.
#
# The Original Code and all software distributed under the License are
# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
# Please see the License for the specific language governing rights and
# limitations under the License.
#
# @APPLE_LICENSE_HEADER_END@
#
######################################################################
# /*!
# @header
# @abstract
# ParseTree class package file.
# @discussion
# This module contains the ParseTree class, a data
# structure for holding the parse tree as generated by
# {@link //apple_ref/perl/instm/HeaderDoc::BlockParse/blockParse//() blockParse}.
#
# See the class documentation below for more details.
# @indexgroup HeaderDoc Parser Pieces
# */
# /*!
# @abstract
# Parse tree (or token therein) generated by parser.
# @discussion
# The ParseTree class is a data structure
# for holding the parse tree as generated by
# {@link //apple_ref/perl/instm/HeaderDoc::BlockParse/blockParse//() blockParse}.
#
# In addition, this class provides a series of
# utility functions for manipulating the
# parse tree, generating output from the
# parse tree in HTML, XML, and plain text
# formats, and extracting Objective-C
# method parameter lists.
#
# @vargroup Node data and tree pointers
# @var TOKEN
# The token itself.
# @var NEXT
# The next (sibling) node in the parse tree.
# @var FIRSTCHILD
# The first child (descendant) node of this node.
# @var PARENT
# The parent node in the parse tree.
# @var lastDisplayNode
# The last node in the parse tree rooted at this node that
# should be displayed. Used only in AppleScript, to hide
# content nested inside functions while still parsing them
# fully. This is used for functions that are not inside
# scripts. For functions that are inside a script statement,
# the lastDisplayNode in the parser state object
# is used instead.
#
# @vargroup File information
# @var FILENAME
# The filename component of the header or source file that contains
# the API content that this parse tree describes. Does not include
# leading path parts.
# @var FULLPATH
# The full (possibly relative) path to the header or source file that
# contains the the API content that this parse tree describes.
# @var INPUTCOUNTER
# The line number where this tree object appeared relative to the
# start of the {@link //apple_ref/perl/cl/HeaderDoc::LineRange LineRange}
# object.
# @var BLOCKOFFSET
# The line number of the first line of the
# {@link //apple_ref/perl/cl/HeaderDoc::LineRange LineRange} object.
# @var LINENUM
# The actual line number in the header ($self->{INPUTCOUNTER} +
# $self->{BLOCKOFFSET}).
#
# @vargroup Administrative information
# @var REFCOUNT
# The number of API objects that reference this parse tree. The
# parse tree cannot be purged until this count goes to zero.
# @var APIOWNERS
# An array of API objects that reference the parse tree rooted in
# this node.
# @var RAWPARSEDPARAMETERS
# An array of parsed parameters associated with this node.
# Each entry in this array is a string containing the
# text of a parameter. This is currently unused.
# @var PARSEDPARAMS
# An array of parsed parameter objects associated with this node.
# Each entry in this array should be a MinorAPIElement object,
# but right now, it's a mixture of objects and strings.
# This is not currrently used for anything, but may be used in
# the future once support is fully integrated in the parser.
# @var PETDONE
# Set to 1 by {@link processEmbeddedTags} to indicate that this
# parse tree has already been processed. This prevents duplicate
# insertion of symbols nested within classes.
#
# @vargroup Token attributes
# @var HIDEMACROLASTTOKEN
# Set to 1 on the space or 2 on the curly brace after a
# macro name. Used to hide the contents of a macro for
# output purposes, depending on flags.
# @var ACCESSCONTROLSTATE
# The access control state in force at the time this node was created.
# @var PARSERSTATE
# A parser state object stored at this node in the parse tree.
# used for reparsing class contents.
# @var HIDDEN
# Indicates that this node should not be emitted in HTML output.
# @var ISAVAILABILITYMACRO
# Indicates that this node is an availability macro. This
# is used for wrapping content in both HTML and XML.
# @var RE_STATE
# Indicates that this node is part of a regular expression.
# This value contains one of the following values:
#
# RE_PREFIX (the optional "command" letters before
# the expression)
#
# RE_START (the opening slash or other delimiter)
#
# RE_PARTSEP (the intermediate delimiter or delimiters)
#
# RE_END (the trailing slash or other delimiter).
#
# Note that the trailing flags are not tagged in any special way
# because you can trivially distinguish the parse tree node that
# contains them by inspecting the node after RE_END.
#
# @vargroup Caches
#
# @var HTMLTREE
# A cached copy of the HTML tree for the parse tree rooted in this node,
# as generated by {@link htmlTree}.
# @var XMLTREE
# A cached copy of the XML tree for the parse tree rooted in this node,
# as generated by {@link xmlTree}.
# @var CPNC
# A cached copy of the results of {@link childpeeknc} for this node.
# @var NTNC
# A cached copy of the results of {@link nextTokenNoComments} for this
# node.
# */
package HeaderDoc::ParseTree;
use strict;
use vars qw($VERSION @ISA);
use HeaderDoc::Utilities qw(isKeyword parseTokens stringToFields doxyTagFilter casecmp emptyHDok complexAvailabilityToArray printHash validTag);
use HeaderDoc::BlockParse qw(blockParse nspaces);
use Carp qw(cluck);
# use WeakRef;
# /*!
# @abstract
# The revision control revision number for this module.
# @discussion
# In the git repository, contains the number of seconds since
# January 1, 1970.
# */
$HeaderDoc::ParseTree::VERSION = '$Revision: 1394133609 $';
################ General Constants ###################################
my $debugging = 0;
my $apioDebug = 0;
# 0 = normal; 1 = debug; 2 = debug + backtraces
my $treeDebug = 0;
my %defaults = (
# TOKEN => undef,
# NEXT => undef,
# FIRSTCHILD => undef,
# APIOWNERS => undef,
# PARSEDPARAMS => undef,
PETDONE => 0,
REFCOUNT => 0,
# XMLTREE => undef,
# HTMLTREE => undef,
# CPNC => undef,
# NTNC => undef,
# RAWPARSEDPARAMETERS => undef,
# PARSERSTATE => undef,
# ACCESSCONTROLSTATE => undef,
# FILENAME => undef,
# LINENUM => undef,
# HIDDEN => undef
);
# /*!
# @abstract
# Creates a new ParseTree object.
# @param param
# A reference to the relevant package object (e.g.
# HeaderDoc::ParseTree->new() to allocate
# a new instance of this class).
# */
sub new {
my($param) = shift;
my($class) = ref($param) || $param;
my %selfhash = %defaults;
my $self = \%selfhash;
# cluck("Parse tree $self created\n");
bless($self, $class);
$self->_initialize();
# Now grab any key => value pairs passed in
my (%attributeHash) = @_;
foreach my $key (keys(%attributeHash)) {
$self->{$key} = $attributeHash{$key};
}
return ($self);
}
# /*!
# @abstract
# Initializes an instance of a ParseTree object.
# @param self
# The object to initialize.
# */
sub _initialize {
my($self) = shift;
$self->{ACCESSCONTROLSTATE} = $HeaderDoc::AccessControlState;
$self->{FILENAME} = $HeaderDoc::headerObject->filename();
$self->{FULLPATH} = $HeaderDoc::headerObject->fullpath();
$self->{LINENUM} = $HeaderDoc::CurLine;
$self->{HIDDEN} = $HeaderDoc::hidetokens;
$self->{APIOWNERS} = ();
$self->{PARSEDPARAMS} = ();
$self->{RAWPARSEDPARAMETERS} = ();
return;
my($self) = shift;
# $self->{TOKEN} = undef;
# $self->{NEXT} = undef;
# $self->{FIRSTCHILD} = undef;
$self->{APIOWNERS} = ();
$self->{ACCESSCONTROLSTATE} = $HeaderDoc::AccessControlState;
$self->{PARSEDPARAMS} = ();
$self->{FILENAME} = $HeaderDoc::headerObject->filename();
$self->{FULLPATH} = $HeaderDoc::headerObject->fullpath();
$self->{LINENUM} = $HeaderDoc::CurLine; # $HeaderDoc::headerObject->linenum();
# $self->{PETDONE} = 0;
$self->{HIDDEN} = $HeaderDoc::hidetokens;
$self->{REFCOUNT} = 0;
# $self->{XMLTREE} = undef;
# $self->{HTMLTREE} = undef;
# $self->{CPNC} = undef;
# # $self->{NPCACHE} = undef;
# $self->{NTNC} = undef;
# # $self->{CTSUB} = undef;
# # $self->{CTSTRING} = undef;
$self->{RAWPARSEDPARAMETERS} = ();
$self->{PARSERSTATE} = undef; # $HeaderDoc::curParserState;
}
my $colorDebug = 0;
# /*!
# @abstract
# Duplicates this ParseTree object into another one.
# @param self
# The object to clone.
# @param clone
# The victim object.
# */
sub clone {
my $self = shift;
my $clone = undef;
if (@_) {
$clone = shift;
} else {
$clone = HeaderDoc::ParseTree->new();
}
# $self->SUPER::clone($clone);
# now clone stuff specific to ParseTree
$clone->{TOKEN} = $self->{TOKEN};
# Note: apiOwner is no longer recursive, so there is no need
# to recursively clone parse trees. Only the top node will
# ever be modified legitimately (except when pruning headerdoc
# comments, but that needs to occur for all instances).
$clone->{FIRSTCHILD} = $self->{FIRSTCHILD};
$clone->{NEXT} = $self->{NEXT};
# $clone->{FIRSTCHILD} = undef;
# if ($self->{FIRSTCHILD}) {
# my $firstchild = $self->{FIRSTCHILD};
# $clone->{FIRSTCHILD} = $firstchild->clone();
# }
# $clone->{NEXT} = undef;
# if ($self->{NEXT}) {
# my $next = $self->{NEXT};
# $clone->{NEXT} = $next->clone();
# }
$clone->{APIOWNERS} = $self->{APIOWNERS};
$clone->{PARSEDPARAMS} = $self->{PARSEDPARAMS};
$clone->{PETDONE} = 0;
return $clone;
}
# /*!
# @abstract
# Adds a sibling to this node in the parse tree.
# @param self
# The current parse tree node.
# @param name
# The token value for the new node.
# @param hide
# Pass 1 if this node (and its children) should be
# hidden when emitting the tree as HTML, else 0.
# @result
# Returns the new sibling node.
# */
sub addSibling
{
my $self = shift;
my $name = shift;
my $hide = shift;
my $newnode = HeaderDoc::ParseTree->new();
my $localDebug = 0;
# print STDERR "addSibling $self\n";
print STDERR "addSibling $self \"$name\" HIDDEN: $hide\n" if ($treeDebug || $localDebug);
if ($treeDebug == 2) { cluck("backtrace"); }
# Always hide siblings of descendants of elements that the parser
# tells us to hide. Also spaces if they are the immediate successor
# to the element right after a hidden element, regardless.
if ($self->hidden() == 2) {
$hide = 2;
} elsif ($name =~ /^\s+$/ && $self->hidden()) {
$hide = 1;
}
print STDERR "HIDE NOW $hide\n" if ($treeDebug || $localDebug);
my $parent = $self->parent;
my $pos = $self;
# if ($parent) {
# $pos = $parent->lastchild();
# bless($pos, "HeaderDoc::ParseTree");
# } else {
# warn "NOPARENTA!\nNOPARENTB!\nNOPARENTC!\n";
bless($pos, "HeaderDoc::ParseTree");
# print STDERR "POS: $pos\n";
# while ($pos && $pos->next()) {
# $pos = $pos->next();
# bless($pos, "HeaderDoc::ParseTree");
# # print STDERR "POS: $pos: ".$pos->token()."\n";
# }
# bless($pos, "HeaderDoc::ParseTree");
# }
$newnode->token($name);
if ($hide) { $newnode->hidden($hide); }
$newnode->parent($parent);
# if ($parent) { $parent->lastchild($newnode); }
my $noderef = $newnode;
my $retval = $pos->next($noderef);
print STDERR "New sibling: ".$retval if ($treeDebug);
return $retval;
# print STDERR "$self (".$self->token().") RET $ret (".$ret->token().")\n";
# return $ret;
}
# /*!
# @abstract
# Adds a child to this node in the parse tree.
# @param self
# The current parse tree node.
# @param name
# The token value for the new node.
# @param hide
# Pass 1 if this node (and its children) should be
# hidden when emitting the tree as HTML, else 0.
# @result
# Returns the new child node.
# */
sub addChild
{
my $self = shift;
my $name = shift;
my $hide = shift;
# print STDERR "addChild\n";
print STDERR "addChild to $self \"$name\"\n" if ($treeDebug);
if ($treeDebug == 2) { cluck("backtrace"); }
# If the parser wants a node hidden, any children of such a node
# should be hidden, as should any siblings of those children or their
# descendants. Siblings of the original node should not, however.
#
# The block parser passes in 3 for $hide to hide the node and its
# descendants. Its descendants have their hidden value set to 2 so
# that their siblings will also be hidden, but the top level node
# still retains the original hidden value of 3.
if ($self->hidden() == 2 || $self->hidden() == 3) { $hide = 2; }
if (!$self->firstchild()) {
my $newnode = HeaderDoc::ParseTree->new();
if ($hide) { $newnode->hidden($hide); }
$newnode->token($name);
my $noderef = $newnode;
$newnode->parent($self);
# $self->lastchild($noderef);
my $retval = $self->firstchild($noderef);
print STDERR "New child: ".$retval if ($treeDebug);
return $retval; # $self->firstchild($noderef);
} else {
warn "addChild called when firstchild exists. Dropping.\n";
# my $node = $self->firstchild();
# bless($node, "HeaderDoc::ParseTree");
# return $node->addSibling($name, $hide);
}
}
# /*! @abstract
# Checks to see if this node is at the same level of the parse tree
# and occurs after the node specified (or is the node specified).
# @param self
# The current node.
# @param node
# The specified node for comparison.
# */
sub isAfter
{
my $self = shift;
my $node = shift;
my $ptr = $node;
while ($ptr) {
if ($ptr == $self) {
return 1;
}
$ptr = $ptr->next();
}
return 0;
}
# /*! @abstract
# Adds an additional API owner for a tree.
# @param self
# This node.
# @param newapio
# The API owner to add.
# */
sub addAPIOwner {
my $self = shift;
my $newapio = shift;
print STDERR "addAPIOwner: SELF WAS $self\n" if ($apioDebug);
print STDERR "addAPIOwner: APIO WAS $newapio\n" if ($apioDebug);
if (!$newapio) {
warn("apiOwner called with empty APIO!\n");
return undef;
} else {
$self->{REFCOUNT}++;
cluck("REFCOUNT $self -> ".$self->{REFCOUNT}." because of $newapio (add)\n") if ($HeaderDoc::debugAllocations);
# weaken($newapio);
push(@{$self->{APIOWNERS}}, $newapio);
}
return $newapio;
}
# /*!
# @abstract
# Replaces an API owner for the tree.
# @param self
# This tree node.
# @param old
# The previous API owner to delete.
# @param new
# The new API owner to insert.
# */
sub apiOwnerSub
{
my $self = shift;
my $old = shift;
my $new = shift;
my $quiet = shift;
my $localDebug = 0;
cluck("apiOwnerSub called with SELF=$self OLD=$old NEW=$new\n") if ($localDebug);
my @arr = ();
my $found = 0;
foreach my $possowner (@{$self->{APIOWNERS}}) {
print "IN apiOwnerSub: $possowner\n" if ($HeaderDoc::debugAllocations);
if ($possowner == $new) {
$new = undef; # lower refcount later.
}
if ($possowner != $old) {
push(@arr, $possowner);
} else {
$found++;
}
}
if (!$found && !$quiet) {
warn("OLD API OWNER NOT FOUND IN apiOwnerSub(). Please file a bug.\n");
}
if ($new) {
push(@arr, $new);
} elsif ($found) {
$self->{REFCOUNT} -= $found;
print STDERR "REFCOUNT $self -> ".$self->{REFCOUNT}." because of $old going away\n" if ($HeaderDoc::debugAllocations);
if (!$self->{REFCOUNT}) {
cluck("Ref count went to zero in apiOwnerSub($self)\n") if ($HeaderDoc::debugAllocations);
}
}
$self->{APIOWNERS} = \@arr;
}
# /*!
# @abstract
# Creates a new state object for use by colorTreeSub.
# @param withref
#
# @param morevalues
# Additional values to add.
#
# @discussion
# This greatly reduces the number of arguments to
# {@link colorTreeSub}. It has no other purpose beyond making
# the code more readable by treating the current
# state as an object.
#
# Keys and legal values are described as local variables for
# {@link colorTreeSub}.
# */
sub newCTState
{
my %statedata = ();
if (@_) {
my $withref = shift;
if ($withref) {
my %withvalues = %{$withref};
foreach my $key (keys %withvalues) {
$statedata{$key} = $withvalues{$key};
}
}
if (@_) {
my (%morevalues) = @_;
foreach my $key (keys(%morevalues)) {
$statedata{$key} = $morevalues{$key};
}
}
}
return \%statedata;
}
# /*!
# @abstract
# Gests/sets the API owner for the tree.
# @param self
# This tree node.
# @param newapio
# The new API owner value. (Optional.)
# */
sub apiOwner {
my $self = shift;
my $localDebug = 0;
if (@_) {
my $newapio = shift;
if (!$newapio) {
warn("apiOwner called with empty APIO!\n");
}
print STDERR "apiOwner: SETTING TO $newapio (".$newapio->rawname().")\n" if ($apioDebug || $localDebug);
$self->{APIOWNERS} = ();
push(@{$self->{APIOWNERS}}, $newapio);
$self->{REFCOUNT} = 1;
print STDERR "REFCOUNT $self -> 1 because of $newapio (set)\n" if ($HeaderDoc::debugAllocations);
}
my $apio = undef;
foreach my $possowner (@{$self->{APIOWNERS}}) {
print STDERR "TESTING $possowner\n" if ($localDebug);
if ($possowner !~ /HeaderDoc::HeaderElement/) {
if ($possowner !~ /HeaderDoc::APIOwner/) {
if ($possowner) {
$apio = $possowner;
print STDERR "CHOSE $apio\n" if ($localDebug);
}
}
}
}
if (!$apio) {
$apio = pop(@{$self->{APIOWNERS}});
push(@{$self->{APIOWNERS}}, $apio);
print STDERR "GUESSING $apio\n" if ($localDebug);
}
# Try this if you run into trouble, but....
# if (!$apio && $self->{PARENT}) {
# return $self->{PARENT}->apiOwner();
# }
return $apio;
}
# /*!
# @abstract
# Returns all of the API owners for the tree.
# @param self
# This tree node.
# */
sub apiOwners
{
my $self = shift;
# foreach my $apio (@{$self->{APIOWNERS}} ) {
# print STDERR "APIOWNERS LIST INCLUDES $apio\n";
# }
return $self->{APIOWNERS};
}
# /*!
# @abstract
# Returns the rightmost sibling of this tree node.
# @param self
# This tree node.
# */
sub lastSibling {
my $self = shift;
while ($self && $self->next()) { $self = $self->next(); }
return $self;
}
# /*!
# @abstract
# Gets/sets the access control state (public, private,
# etc.) for this tree node.
# @param self
# This tree node.
# @param ACCESSCONTROLSTATE
# The new access control value to set. (Optional.)
# */
sub acs {
my $self = shift;
if (@_) {
$self->{ACCESSCONTROLSTATE} = shift;
}
return $self->{ACCESSCONTROLSTATE};
}
# /*!
# @abstract
# Gets/sets the token for this tree node.
# @param self
# This tree node.
# @param TOKEN
# The new token value to set. (Optional.)
# */
sub token {
my $self = shift;
if (@_) {
$self->{TOKEN} = shift;
}
return $self->{TOKEN};
}
# /*!
# @abstract
# Gets/sets whether this node and its children
# should be hidden when emitting HTML.
# @discussion
# If a new value is specified, this function
# recursively modifies its descendants.
# @param self
# This tree node.
# @param value
# The new token value to set. (Optional.)
# */
sub hidden {
my $self = shift;
if (@_) {
my $value = shift;
$self->{HIDDEN} = $value;
my $fc = $self->firstchild();
if ($fc) { $fc->hiddenrec($value); }
}
return $self->{HIDDEN};
}
# /*!
# @abstract
# Sets whether this node, its children, and its
# siblings should be hidden when emitting HTML.
# @discussion
# This function recursively modifies its
# descendants and siblings.
# @param self
# This tree node.
# @param value
# The new token value to set.
# */
sub hiddenrec
{
my $self = shift;
my $value = shift;
# print STDERR "SETTING HIDDEN VALUE OF TOKEN ".$self->token()." to $value\n";
# $self->hidden($value);
$self->{HIDDEN} = $value;
my $fc = $self->firstchild();
if ($fc) { $fc->hiddenrec($value); }
my $nx = $self->next();
if ($nx) { $nx->hiddenrec($value); }
}
# /*!
# @abstract
# Scrapes the parameters for an Objective-C method from
# the parse tree and returns an array.
# @param self
# The parse tree for the method.
# */
sub objCparsedParams()
{
my $self = shift;
my $lang = shift;
my $sublang = shift;
my @parsedParams = ();
my $objCParmDebug = 0;
my $inType = 0;
my $inName = 0;
my $position = 0;
my $curType = "";
my $curName = "";
my $cur = $self;
my @stack = ();
my $eoDec = 0;
my $lastTag = "";
my $tagName = "";
my $noParse = 1;
my $noTag = 1;
while ($cur || scalar(@stack)) {
while (!$cur && !$eoDec) {
if (!($cur = pop(@stack))) {
$eoDec = 1;
} else {
$cur = $cur->next();
}
}
if ($eoDec) { last; }
# process this element
my $token = $cur->token();
if ($token eq ":") {
if (!$noTag) {
$tagName = $lastTag;
}
$noParse = 0;
} elsif ($noParse) {
# drop token on the floor. It's part of the name.
} elsif ($token eq "(") {
$inType++;
$curType .= $token;
} elsif ($token eq ")") {
if (!(--$inType)) {
$inName = 1;
$noTag = 0;
}
$curType .= $token;
} elsif ($token =~ /^[\s\W]/o && !$inType) {
# drop white space and symbols on the floor (except
# for pointer types)
if ($inName && ($curName ne "")) {
$inName = 0;
my $param = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang);
$param->linenuminblock($self->apiOwner()->linenuminblock());
$param->blockoffset($self->apiOwner()->blockoffset());
# $param->linenum($self->apiOwner()->linenum());
$param->outputformat($self->apiOwner()->outputformat());
$param->tagname($tagName);
$param->name($curName);
$param->type($curType);
$param->position($position++);
print STDERR "ADDED $curType $curName [$tagName]\n" if ($objCParmDebug);
$curName = "";
$curType = "";
push(@parsedParams, $param);
$noParse = 1;
}
} elsif ($inType) {
$curType .= $token;
} elsif ($inName) {
$curName .= $token;
}
my $fc = $cur->firstchild();
if ($fc) {
push(@stack, $cur);
$cur = $fc;
} else {
$cur = $cur->next();
}
if ($token =~ /\w/) {
$lastTag = $token;
}
}
if ($objCParmDebug) {
foreach my $parm (@parsedParams) {
print STDERR "OCCPARSEDPARM: ".$parm->type()." ".$parm->name()."\n";
}
}
return @parsedParams;
}
# /*!
# @abstract
# Sets the parsed parameter list for this node or
# searches the parse tree for parsed parameter lists and
# reurns the result.
# @param self
# This object.
# @param pplref
# A reference to a parsed parameter list. (Optional.)
# @discussion
# This subroutine is for future transition. The end goal is to
# move the parsed parameter support from the HeaderElement level
# entirely into the parse tree.
# */
sub parsedParams($)
{
my $self = shift;
my $lang = shift;
my $sublang = shift;
my @array = ();
# print STDERR "parsedParams called\n";
if (@_) {
if ($self->apiOwner() eq "HeaderDoc::Method") {
@{$self->{PARSEDPARAMS}} = $self->objCparsedParams($lang, $sublang);
} else {
my $pplref = shift;
# @{$self->{PARSEDPARAMS}} = @_;
@{$self->{PARSEDPARAMS}} = @{$pplref};
# for my $parm (@{$pplref}) {
# print STDERR "ADDING PARM $parm\n";
# }
}
}
if (!($self->{PARSEDPARAMS})) {
# print STDERR "PARSEDPARAMS PROBLEM: TOKEN WAS ".$self->token()."\n";
# print STDERR "PRINTTREE:\n";
# $self->printTree();
# print STDERR "ENDOFTREE\n";
my $next = $self->next();
if ($next) { return $next->parsedParams($lang, $sublang); }
else { return undef; }
# else { die("Can't find parsed params\n"); }
}
# print STDERR "HERE: $self : ". $self->token." : ".$self->{PARSEDPARAMS}."\n";
# foreach my $parm (@{$self->{PARSEDPARAMS}}) {
# print STDERR "FOUND PARM $parm\n";
# }
return @{$self->{PARSEDPARAMS}};
}
# /*!
# @abstract
# Finds the previous node by laborious tree walking of
# the parent node's children.
# @discussion
# This function should be avoided if at all possible.
# It is currently unused, though it was used in the
# distant past. It is retained for completeness.
# @param self
# This tree node.
# */
sub slowprev()
{
my $self = shift;
my $parent = $self->parent;
if (!$parent) { return undef; }
my $fc = $parent->firstchild;
if ($self == $fc) { return undef; }
while ($fc && $fc->next && ($fc->next != $self)) { $fc = $fc->next; }
return $fc;
}
# /*!
# @abstract
# Copies a parsed parameter list and sets the parsed parameter
# list for this tree node to be a reference to the resulting
# array.
# @param self
# This tree node.
# @param pplref
# A reference to the parsed parameter list array to copy.
# */
sub parsedParamCopy
{
my $self = shift;
my $pplref = shift;
my $lang = shift;
my $sublang = shift;
my $localDebug = 0;
my @parms = @{$pplref};
my @newparms = ();
foreach my $parm (@parms) {
push(@newparms, $parm);
}
$self->parsedParams($lang, $sublang, \@newparms);
print STDERR "PARSEDPARAMCOPY -> $self\n" if ($localDebug);
print STDERR "TOKEN WAS ".$self->token()."\n" if ($localDebug);
}
# /*!
# @abstract
# Handles embedded HeaderDoc markup, returning a list
# of parameters, constants, etc.
# @param self
# The top of the parse tree/subtree to scan.
# @param xmlmode
# Set to 1 if output mode is XML.
# @param apiOwner
# If this parse tree is owned by an API owner, this
# variable indicates the primary API owner to which
# the resulting objects should be added. If this
# parse tree is owned by a non-API-oowner object (e.g.
# a Struct object), this value is unused.
# */
sub processEmbeddedTags
{
my $self = shift;
my $xmlmode = shift;
my $apiOwner = shift; # Always subclass of APIOwner, used as "true" apio value for calling APIOprocessEmbeddedTagsRec
my $apiolist = $self->apiOwners();
my $apio = $self->apiOwner();
# $self->printTree();
# $self->dbprint();
my $localDebug = 0;
# if ($apio->isAPIOwner()) { $localDebug = 1; }
print STDERR "IN PET\n" if ($localDebug);
print STDERR "API IS $apio\n" if ($localDebug);
print STDERR $apio->name()."\n" if ($localDebug);
print STDERR "APIOLIST IS $apiolist\n" if ($localDebug);;
if ($localDebug && $apiolist) {
for my $tempapio (@{$apiolist}) {
print STDERR "RETURNED APIOLIST INCLUDES $tempapio (".($tempapio->rawname()).")\n";
}
}
### Make the APIO owner list unique. ###
# print STDERR "APIOLIST:\n";
my %newapiohash = ();
foreach my $tempapio (@{$apiolist}) {
# print $tempapio."\n";
$newapiohash{$tempapio} = $tempapio;
}
# print STDERR "END APIOLIST\n";
my @newapiolist = ();
foreach my $tempapio (keys %newapiohash) {
push(@newapiolist, $newapiohash{$tempapio});
}
$apiolist = \@newapiolist;
if ($self->{PETDONE}) {
print STDERR "SHORTCUT\n" if ($localDebug);
return;
}
$self->{PETDONE} = 1;
if (!$apio) { return; }
my $apioclass = ref($apio) || $apio;
my $old_enable_cpp = $HeaderDoc::enable_cpp;
if ($apioclass =~ /HeaderDoc::PDefine/ && $apio->parseOnly()) {
if ($HeaderDoc::enable_cpp) {
print STDERR "CPP Enabled. Not processing comments embedded in #define macros marked as 'parse only'.\n" if ($localDebug);
return;
}
} elsif ($apioclass =~ /HeaderDoc::PDefine/) {
if ($HeaderDoc::enable_cpp) {
print STDERR "Temporarily disabling CPP.\n" if ($localDebug);
$HeaderDoc::enable_cpp = 0;
}
}
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $macronamesref,
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $soconstructor) = parseTokens($apio->lang(), $apio->sublang());
# @_;
my %parseTokens = %{parseTokens($apio->lang(), $apio->sublang())};
my $eoDeclaration = 1;
my $lastDeclaration = "";
my $curDeclaration = "";
my $sodec = $self;
my $pendingHDcomment = "";
my ($case_sensitive, $keywordhashref) = $apio->keywords();
my $lastnode = undef;
my $parserState = $self->parserState();
if ($parserState) {
print STDERR "PARSERSTATE\n" if ($localDebug);
$lastnode = $parserState->{lastTreeNode};
print STDERR "LASTNODE: $lastnode\n" if ($localDebug);
if ($lastnode && $localDebug) { print STDERR "LASTNODE TEXT: \"".$lastnode->token()."\"\n"; }
}
my $enable_javadoc_comments = $HeaderDoc::parse_javadoc || ($apio->lang() eq "java");
# print STDERR "EJC: $enable_javadoc_comments\n";
if ($apio->isAPIOwner()) {
print STDERR "Owner is APIOwner. Using APIOprocessEmbeddedTagsRec for parse tree $self.\n" if ($localDebug);
my $hashtreecur = undef;
my $hashtreeroot = undef;
print STDERR "BEFORE APIOprocessEmbeddedTagsRec for $apio (".$apio->name().")\n" if ($localDebug > 1);
print STDERR "PS: $parserState\n" if ($localDebug > 1);
cluck("bt\n") if ($localDebug > 1);
$self->dbprint() if ($localDebug > 1);
# $self->printTree() if ($localDebug > 1);
$self->APIOprocessEmbeddedTagsRec($apiOwner, $case_sensitive, \%parseTokens, $lastnode, 0, 1, $enable_javadoc_comments, $xmlmode, $apio->lang(), $apio->sublang(), $hashtreecur, $hashtreeroot);
} else {
print STDERR "calling processEmbeddedTagsRec for $apio (".$apio->name().") for parse tree $self.\n" if ($localDebug);
$self->processEmbeddedTagsRec($xmlmode, $eoDeclaration, \%parseTokens, $case_sensitive, $keywordhashref, $lastDeclaration, $curDeclaration, $pendingHDcomment,
$apio, $apiolist, $sodec, $lastnode, $enable_javadoc_comments, undef);
}
print STDERR "PETDONE\n" if ($localDebug);
$HeaderDoc::enable_cpp = $old_enable_cpp;
return;
}
# /*!
# @abstract
# Gets the name and the corresponding comment
# field type (e.g. \@struct) for a given
# declaration
# @discussion
# This helper reduces the amount of code needed
# to use the block parser in several places in
# the parse tree code by consolidating this
# logic in one place.
# @param self
# This parse tree object.
# @param string
# The declaration to parse.
# @param apio
# The object (may be API owner or not) to which
# the resulting objects should be added.
# @param typedefname
# The keyword typedef or equivalent.
# @param case_sensitive
# Set to 0/1 depending on whether the current
# programming language uses case-sensitive
# token matching.
# @param keywordhashref
# A reference to the keyword hash for this
# language (obtained with a call to the
# {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/keywords//() keywords}
# function).
# */
sub getNameAndFieldTypeFromDeclaration
{
my $self = shift;
my $string = shift;
my $apio = shift;
my $typedefname = shift;
my $case_sensitive = shift;
my $keywordhashref = shift;
my $localDebug = 0;
my $inputCounter = 0;
my $fullpath = $apio->fullpath();
my $linenum = $apio->linenum();
my $lang = $apio->lang();
my $sublang = $apio->sublang();
my $blockoffset = $linenum;
my $argparse = 2;
if ($string !~ /\S/) { return ("", ""); }
# This never hurts just to make sure the parse terminates.
# Be sure to add a newline before the semicolon in case
# there's an inline comment at the end.
$string .= "\n;\n";
print STDERR "STRING WAS $string\n" if ($localDebug);
cluck("getNameAndFieldTypeFromDeclaration backtrace\n") if ($localDebug);
my @lines = split(/\n/, $string);
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $macronameref,
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $soconstructor) = parseTokens($lang, $sublang);
# my @newlines = ();
foreach my $line (@lines) {
$line .= "\n";
# push(@newlines, $line);
# print STDERR "LINE: $line\n" if ($localDebug);
}
# @lines = @newlines;
# my ($case_sensitive, $keywordhashref) = $apio->keywords();
my $lastlang = $HeaderDoc::lang;
my $lastsublang = $HeaderDoc::sublang;
$HeaderDoc::lang = $apio->lang;
$HeaderDoc::sublang = $apio->sublang;
my ($inputCounter, $declaration, $typelist, $namelist, $posstypes, $value, $pplStackRef, $returntype, $privateDeclaration, $treeTop, $simpleTDcontents, $availability, $fileoffset, $conformsToList) = blockParse($fullpath, $blockoffset, \@lines, $inputCounter, $argparse, \%HeaderDoc::ignorePrefixes, \%HeaderDoc::perHeaderIgnorePrefixes, \%HeaderDoc::perHeaderIgnoreFuncMacros, $keywordhashref, $case_sensitive, $apio->lang, $apio->sublang);
# print "DEC WAS $string\n";
# print "DEC RETURNED WAS $declaration\n";
$HeaderDoc::lang = $lastlang;
$HeaderDoc::sublang = $lastsublang;
# @@@ FIX UP TYPES HERE
print STDERR "IC:$inputCounter DEC:$declaration TL:$typelist NL:$namelist PT:$posstypes VAL:$value PSR:$pplStackRef RT:$returntype PD:$privateDeclaration TT:$treeTop STC:$simpleTDcontents AV:$availability\n" if ($localDebug);
$self->parsedParamCopy($pplStackRef, $lang, $sublang);
my $name = $namelist;
$name =~ s/^\s*//so; # ditch leading spaces
$name =~ s/\s.*$//so; # ditch any additional names. (There shouldn't be any)
# print STDERR "NAME WAS $name\n";
my $typestring = $typelist . $posstypes;
print STDERR "TS: $typestring\n" if ($localDebug);
my $type = "\@constant";
if ($typestring =~ /^(function|method|ftmplt|operator|callback)/o) {
$type = "\@$1";
if ($typestring =~ /(ftmplt|operator)/) { $type = "\@function"; }
# $type = "\@callback";
} elsif ($typestring =~ /^(class|interface|module|category|protocol)/o) {
$typestring =~ s/^ *//;
$type = "\@$typestring";
$type =~ s/ .*$//;
} elsif ($typestring =~ /^(struct|union|record|enum|typedef)/o || (($typedefname ne "") && $typestring =~ /^$typedefname/)) {
$type = "\@field";
} elsif ($typestring =~ /(MACRO|#define)/o) {
$type = "\@field";
if ($apio eq "HeaderDoc::PDefine") {
# The @defineblock case
$type = "\@define";
}
} elsif ($typestring =~ /(constant)/o) {
$type = "\@constant";
print STDERR "VALUE: \"$value\"\n" if ($localDebug);
if (($value eq "")) {
# It's just a variable.
if ($apio =~ /HeaderDoc::Enum/) {
$type = "\@constant"; # Still a constant, albeit with an unknown value.
} else {
$type = "\@field";
}
}
} else {
warn "getNameAndFieldTypeFromDeclaration: UNKNOWN TYPE ($typestring) RETURNED BY BLOCKPARSE\n";
print STDERR "STRING WAS $string\n" if ($localDebug);
}
if (!$name || ($name eq "")) {
warn "COULD NOT GET NAME FROM DECLARATION. DECLARATION WAS:\n$string\n";
return ("", "");
}
print STDERR "TYPE $type, NAME $name\n" if ($localDebug);
return ($name, $type);
}
# /*!
# @abstract
# Returns whether to process comments in the children of a node
# with a given token.
# @discussion
# The tree walking code explicitly avoid things like strings
# and regular expressions without the need to search for them by
# disallowing children of any non-word, non-whitespace characters
# other than parentheses, curly braces, and colons.
#
# @param token
# The token to check.
# @param soc
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param eoc
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param ilc
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param ilc_b
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param lbrace
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param case_sensitive
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/keywords//() keywords}.
# */
sub commentsNestedIn
{
my $token = shift;
my $soc = shift;
my $eoc = shift;
my $ilc = shift;
my $ilc_b = shift;
my $lbrace = shift;
my $case_sensitive = shift;
# if ($token eq $soc || $token eq $eoc || $token eq $ilc || $token eq $ilc_b) { return 1; }
if ($token =~ /\W/o) {
if ($token =~ /[{(}):]/o) { return 1; }
if ($token =~ /^#/o) { return 2; }
if (casecmp($token, $lbrace, $case_sensitive)) { return 1; }
if ($token =~ /\s/o) { return 1; }
return 0;
}
# if (casecmp($token, $lbrace, $case_sensitive)) { return 1; }
return 1;
}
# /*!
# @abstract
# Processes embedded tags within an API owner (class).
# @discussion
# Unlike {@link processEmbeddedTagsRec}, this function
# does significantly less work because the code in APIOwner.pm
# handles most of the effort, thanks in part to the pots of
# parser gold hidden in the parse tree's hollow.
#
# This function also works differently because instead of
# adding fields to a struct or whatever, it is adding
# functions, data types, variables, and even classes
# that are part of another class.
# */
sub APIOprocessEmbeddedTagsRec($$$$$$$$)
{
my $self = shift;
my $apiOwner = shift; # my $apiolist = shift;
my $case_sensitive = shift;
my $parseTokensRef = shift;
my $lastTreeNode = shift;
my $skipchildren = shift;
my $isroot = shift; # Don't add parse tree to the parent object twice.
my $enable_javadoc_comments = shift;
my $xmlmode = shift;
my $lang = shift;
my $sublang = shift;
my $hashtreecur = shift;
my $hashtreeroot = shift;
my %parseTokens = %{$parseTokensRef};
# print STDERR "EJC: $enable_javadoc_comments\n";
my $localDebug = 0;
if (1 && $localDebug) {
my $apio = $self->apiOwner();
if ($apio) {
# if ($apio->name() eq "OSObject") {
print STDERR "DUMPING TREE.\n"; $self->dbprint();
# }
}
}
my $continue = 1;
if ($self == $lastTreeNode) {
$continue = 0;
print STDERR "CONTINUE -> 0\n" if ($localDebug);
# return $continue; # Added this to try to fix a bug, but wasn't needed.
}
my $token = $self->token();
my $firstchild = $self->firstchild();
my $next = $self->next();
print STDERR "APIOprocessEmbeddedTagsRec: TOKEN IS \"".$self->token()."\"\n" if ($localDebug);
my $handled = 0;
print STDERR "SOC: \"$parseTokens{soc}\" ILC: \"$parseTokens{ilc}\" ILC_B: \"$parseTokens{ilc_b}\"\n" if ($localDebug);
if ((length($parseTokens{soc}) && ($token eq $parseTokens{soc})) ||
(length($parseTokens{ilc}) && ($token eq $parseTokens{ilc})) ||
(length($parseTokens{ilc_b}) && ($token eq $parseTokens{ilc_b}))) {
if ((($token eq $parseTokens{soc} && $parseTokens{soc}) || ($token eq $parseTokens{ilc} && $parseTokens{ilc})) && $firstchild) {
print STDERR "COMMENT CHECK\n" if ($localDebug);
my $ntoken = $firstchild->token();
my $nntoken = "";
if ($firstchild->next()) {
$nntoken = $firstchild->next()->token();
}
# The parser changed behavior. The folowing is probably the only relevant behaviour,
# but the previous behavior is preserved for partial inter-module backwards compatibility.
if ($ntoken eq "" && $firstchild->next()) {
$ntoken = $firstchild->next()->token();
if ($firstchild->next()->next()) {
$nntoken = $firstchild->next()->next()->token();
} else {
$nntoken = "";
}
}
# print STDERR "FC: ".$firstchild->token()."\n";
# print STDERR "FCN: ".$firstchild->next()->token()."\n";
# print STDERR "FCNN: ".$firstchild->next()->next()->token()."\n";
if ($lang eq "ruby" || $lang eq "python") {
my $node = $firstchild;
while ($node && ($node->token() !~ /\S/)) {
$node = $node->next();
}
if ($node) { $nntoken = $node->token(); }
# print STDERR "HERE: N = \"$ntoken\" NN = \"$nntoken\"\n";
# if ($ntoken eq "\n" && $nntoken eq "!" && $firstchild->next()->next()->next() &&
# $firstchild->next()->next()->next()->next() &&
# $firstchild->next()->next()->next()->next()->next()) {
# my $nnntoken = $firstchild->next()->next()->next()->token();
# my $nnnntoken = $firstchild->next()->next()->next()->next()->token();
# print STDERR "HERE: NNN = \"$nnntoken\" NNNN = \"$nnnntoken\"\n";
# if ($nnntoken =~ /headerdoc/i && $nnnntoken eq "!") {
# $ntoken = "!";
# } else {
# $ntoken = "";
# }
# } else {
# $ntoken = "";
# }
if ($nntoken =~ /\s*!headerdoc!/i) {
$ntoken = "!";
} else {
$ntoken = "";
}
}
print STDERR "NTOKEN: $ntoken\n" if ($localDebug);
print STDERR "NNTOKEN: $nntoken\n" if ($localDebug);
if ((($lang ne "tcl" && ($ntoken eq "!" || ($enable_javadoc_comments && $ntoken eq "*" && $nntoken !~ /^\*/))) ||
($lang eq "tcl" && ($ntoken eq "/*" && $nntoken eq "!" && $firstchild->next()->next()->next()))) &&
($lang eq "applescript" || (!$self->hidden()))) {
print STDERR "NODECHECK: $self\n" if ($localDebug);
# HDCOMMENT
$self->dbprint() if ($localDebug);
my $string = $token.$firstchild->textTree();
# print STDERR "STRING IS $string\n";
if ($lang eq "ruby" || $lang eq "python") {
$string = $firstchild->next()->next()->textTree();
$string =~ s/^\s*!headerdoc!\s*//si;
$string = "/*!".$string;
$token = $parseTokens{soc};
# print STDERR "STRING IS $string\n";
} elsif ($lang eq "tcl") {
# Scripting languages work differently.
$string = "/*!".$firstchild->next()->next()->next()->textTree();
# $self->dbprint();
# warn("PreString: $string\n");
my $foundend = 0;
if ($string =~ /\*\//) { $foundend = 1; }
my $pos = $self->next();
# warn("NEXT TEXT: ".$pos->textTree()."\n");
while ((!$foundend) && $pos) {
print "MOVING TO NEXT. POS IS $pos TOKEN IS ".$pos->token()."\n" if ($localDebug);
my $temp = $pos->firstchild();
if ($temp) {
my $tempstr = $temp->textTree();
if ($pos->token() eq "#") {
$string .= $tempstr;
} elsif ($tempstr =~ /\S/) {
$foundend;
} else {
$string .= "\n";
}
} else {
$string .= "\n";
}
if ($string =~ /\*\//) { $foundend = 1; }
$pos = $pos->next();
}
# die("PostString: $string\n");
} elsif ($lang eq "applescript") {
$string =~ s/^\s*\(\*/\/\*/s;
}
if ($ntoken eq "*") { $string =~ s/^\s*\/\*\*/\/\*\!/s; }
print STDERR "FOUND HDCOMMENT:\n$string\nEND HDCOMMENT\n" if ($localDebug);
# $string =~ s/^\/[\*\/]\!//s;
# $string =~ s/^\s*//s;
if ($token eq $parseTokens{soc} && $parseTokens{soc}) {
my $eoc = $parseTokens{eoc};
$string =~ s/\Q$eoc\E\s*$//s;
}
print STDERR "STRING IS $string\n" if ($localDebug);
# @@@ DAG CHECK ME
# $string =~ s/^\s*\*\s*//mg;
my $fieldref = stringToFields(doxyTagFilter($string), $self->fullpath, $self->linenum, $xmlmode, $lang, $sublang);
# print STDERR "APIOLIST AT INSERT IS $apiolist\n" if ($localDebug);
# foreach my $owner (@{$apiolist}) {
# print STDERR "X POSSOWNER of $self: $owner\n" if ($localDebug);
# }
# foreach my $owner (@{$apiolist}) {
print STDERR "POSSOWNER of $self: $apiOwner\n" if ($localDebug);
if ($apiOwner && $apiOwner->isAPIOwner()) {
print STDERR "ADDING[1] TO $apiOwner.\n" if ($localDebug);
my $ncurly;
($ncurly, $hashtreecur, $hashtreeroot) = $apiOwner->processComment($fieldref, 1, $self->nextTokenNoComments($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, 1, $enable_javadoc_comments), $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
# We have found the correct level. Anything
# nested deeper than this is bogus (unless we hit a curly brace).
print STDERR "skipochildren -> 1 [1]" if ($localDebug);
$skipchildren = 1;
if ($next) {
$next = $next->skipcurly($parseTokens{lbrace}, $ncurly); # nextTokenNoComments($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, 0, $enable_javadoc_comments);
}
if ($localDebug) {
print STDERR "NEXT IS $next (";
if ($next) {print STDERR $next->token(); }
print STDERR ")\n";
}
}
# }
$handled = 1;
}
} elsif ($firstchild && $firstchild->next() && $firstchild->next()->next()) {
my $pos = $firstchild->next();
my $fcntoken = $pos->token();
while ($fcntoken =~ /\s/ && $pos) {
$pos = $pos->next;
$fcntoken = $pos->token();
}
if (($fcntoken eq $parseTokens{soc} && $parseTokens{soc}) || ($fcntoken eq $parseTokens{ilc} && $parseTokens{ilc})) {
my $fcnntoken = $firstchild->next()->next()->token();
my $fcnnntoken = "";
if ($firstchild->next()->next()->next()) {
$fcnnntoken = $firstchild->next()->next()->next()->token();
}
if ($fcnntoken eq "!" || ($enable_javadoc_comments && $fcnntoken eq "*" && $fcnnntoken !~ /^\*/)) {
# HDCOMMENT
my $string = $fcntoken.$firstchild->textTree();
if ($fcnntoken eq "*") { $string =~ s/^\s*\/\*\*/\/\*\!/s; }
print STDERR "FOUND HDCOMMENT:\n$string\nEND HDCOMMENT\n" if ($localDebug);
# $string =~ s/^\Q$fcntoken\E//s;
# $string =~ s/^\s*\/[\*\/]\!//s;
# $string =~ s/^\s*//s;
if ($fcntoken eq $parseTokens{soc} && $parseTokens{soc}) {
my $eoc = $parseTokens{eoc};
$string =~ s/\Q$eoc\E\s*$//s;
}
# @@@ DAG CHECKME LEADING STARS
# $string =~ s/^\s*\*\s*//mg;
my $fieldref = stringToFields(doxyTagFilter($string), $self->fullpath, $self->linenum, $xmlmode, $lang, $sublang);
# foreach my $owner (@{$apiolist}) {
print STDERR "POSSOWNER of $self: $apiOwner\n" if ($localDebug);
if ($apiOwner && $apiOwner->isAPIOwner()) {
print STDERR "ADDING[2] TO $apiOwner.\n" if ($localDebug);
my $ncurly;
($ncurly, $hashtreecur, $hashtreeroot) = $apiOwner->processComment($fieldref, 1, $self->nextTokenNoComments($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, 1, $enable_javadoc_comments), $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
print STDERR "skipochildren -> 1 [2]" if ($localDebug);
$skipchildren = 1;
# skip the current declaration before
# processing anything else to avoid
# bogus warnings from nested
# HeaderDoc comments.
$next = $next->skipcurly($parseTokens{lbrace}, $ncurly); # nextTokenNoComments($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, 0, $enable_javadoc_comments);
if ($localDebug) {
print STDERR "NEXT IS $next (";
if ($next) {print STDERR $next->token(); }
print STDERR ")\n";
}
}
# }
$handled = 1;
}
}
}
}
if (!$handled && $self->parserState() && !$self->parserState()->{APIODONE} && $HeaderDoc::process_everything && !$isroot) {
print STDERR "Declaration without markup\n" if ($localDebug);
# foreach my $owner (@{$apiolist}) {
# print STDERR "X POSSOWNER of $self: $apiOwner\n" if ($localDebug);
# }
# foreach my $apiOwner (@{$apiolist}) {
print STDERR "POSSOWNER of $self: $apiOwner\n" if ($localDebug);
# Found an embedded declaration that is not tagged.
my @fields = ();
if ($apiOwner && $apiOwner->isAPIOwner()) {
my $ncurly;
($ncurly, $hashtreecur, $hashtreeroot) = $apiOwner->processComment(\@fields, 1, $self, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
$handled = 1;
} else {
warn "$apiOwner is not API Owner\n";
}
# }
}
if ($handled && $localDebug) {
print STDERR "ADDED TREE TO $apiOwner (".$apiOwner->name().")\n";
$self->dbprint();
print STDERR "DUMPING API OWNER:\n";
$apiOwner->dbprint();
print STDERR "END DUMP.\n";
}
# print STDERR "PS: ".$self->parserState()."\n";
# If we get here, we weren't a skipped brace, so we can start nesting again.
if (length($parseTokens{lbrace}) && $token eq $parseTokens{lbrace}) {
print STDERR "skipochildren -> 0 [3]" if ($localDebug);
$skipchildren = 0;
}
if ($firstchild && !$skipchildren) {
print STDERR "APIOprocessEmbeddedTagsRec: MAYBE GOING TO CHILDREN\n" if ($localDebug);
my $nestallowed = commentsNestedIn($token, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $parseTokens{lbrace}, $case_sensitive);
if ($nestallowed) {
print STDERR "APIOprocessEmbeddedTagsRec: YUP. CHILDREN.\n" if ($localDebug);
my $newcontinue;
($newcontinue, $hashtreecur, $hashtreeroot) = $firstchild->APIOprocessEmbeddedTagsRec($apiOwner, $case_sensitive, $parseTokensRef, $lastTreeNode, $skipchildren, 0, $enable_javadoc_comments, $xmlmode, $lang, $sublang, $hashtreecur, $hashtreeroot);
if ($continue) { $continue = $newcontinue; }
print STDERR "Back from Child\n" if ($localDebug);
print STDERR "skipochildren -> $skipchildren [RECURSEOUT]" if ($localDebug);
}
}
if ($next && $continue) {
print STDERR "APIOprocessEmbeddedTagsRec: GOING TO NEXT\n" if ($localDebug);
($continue, $hashtreecur, $hashtreeroot) = $next->APIOprocessEmbeddedTagsRec($apiOwner, $case_sensitive, $parseTokensRef, $lastTreeNode, $skipchildren, 0, $enable_javadoc_comments, $xmlmode, $lang, $sublang, $hashtreecur, $hashtreeroot);
print STDERR "Back from Next\n" if ($localDebug);
}
print STDERR "HTC: $hashtreecur, HTR: $hashtreeroot\n" if ($localDebug);
print STDERR "SN: ".$self->next()." (".($self->next() ? $self->next()->token() : "").")\n" if ($localDebug);
print STDERR "RECURSEOUT (CONTINUE is $continue)\n" if ($localDebug);
return ($continue, $hashtreecur, $hashtreeroot);
}
my $extDebug = 0;
# /*!
# @abstract
# Recursively walks the parse tree looking for HeaderDoc markup
# and processes it.
# @discussion
# This subroutine processes the parse tree recursively looking for
# (and subsequently processing) embedded headerdoc markup, then parses
# the associated declarations. This is a helper function that does
# the actual work for processEmbeddedTags.
# */
sub processEmbeddedTagsRec
{
my $self = shift;
my $xmlmode = shift;
my $eoDeclaration = shift;
my $parseTokensRef = shift;
my $case_sensitive = shift;
my $keywordhashref = shift;
my $lastDeclaration = shift;
my $curDeclaration = shift;
my $pendingHDcomment = shift;
my $apio = shift;
my $apiolist = shift;
my $sodec = shift;
my $lastTreeNode = shift;
my $enable_javadoc_comments = shift;
my $ASterminator = shift;
my %parseTokens = %{$parseTokensRef};
my $localDebug = 0 || $extDebug;
my $fieldTypeDebug = 0;
my $oldCurDeclaration = $curDeclaration;
my $oldsodec = $sodec;
my $ntoken = $self->nextpeeknc($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b});
my $skipchildren = 0;
my $oldPHD = $pendingHDcomment;
my $do_process = 0;
my $continue = 1;
my $inBlockDefine = 0;
my $dropinvalid = 0;
my $lastsodec = $sodec;
my $nextsodec = $sodec;
my $hashtreecur = undef;
my $hashtreeroot = undef;
print STDERR "PETREC\n" if ($localDebug);
print STDERR "PENDING COMMENT: $pendingHDcomment\n" if ($localDebug);
print STDERR "LAST DECLARATION: $lastDeclaration\n" if ($localDebug);
if (!$self) { return ($eoDeclaration, $pendingHDcomment, $continue, $ASterminator); }
my $apioclass = ref($apio) || $apio;
if ($apioclass =~ /HeaderDoc::PDefine/) {
# print STDERR "HDPDEF: ISBLOCK: ".$apio->isBlock()." inDefineBlock: ".$apio->inDefineBlock().".\n";
if ($apio->inDefineBlock() || $apio->isBlock()) {
$inBlockDefine = 1;
my $x = pop(@{$apiolist});
$do_process = $x;
if ($x) {
push(@{$apiolist}, $x);
}
}
}
if ($apio->lang() eq "applescript") {
if (!$ASterminator) {
if ($self->token eq "(") {
$ASterminator = $self;
}
}
}
if ($self == $lastTreeNode) {
print STDERR "CONTINUE -> 0\n" if ($localDebug);
$continue = 0;
}
# print STDERR "lastDec: $lastDeclaration\ncurDec: $curDeclaration\neoDec: $eoDeclaration\n" if ($localDebug);
# Walk the tree.
my $token = $self->token();
$curDeclaration .= $token;
# Used to turn on debugging at a given symbol.
# if ($token eq "kDNSServiceFlagsSuppressUnusable") { $localDebug = 1; $extDebug = 1; }
print STDERR "TOKEN: $token\n" if ($localDebug);
# if ($token !~ /\s/o) { print STDERR "TOKEN: \"$token\" SOC: \"$parseTokens{soc}\" ILC: \"$parseTokens{ilc}\" ILC_B: \"$parseTokens{ilc_b}\".\n"; }
if (($token eq $parseTokens{soc} && $parseTokens{soc}) || ($token eq $parseTokens{ilc} && $parseTokens{ilc}) || ($token eq $parseTokens{ilc_b} && $parseTokens{ilc_b})) {
my $firstchild = $self->firstchild();
print STDERR "Comment start\n" if ($localDebug);
if ($firstchild) {
print STDERR "FCT: ".$firstchild->token()."\n" if ($localDebug);
my $nextchild = $firstchild->next();
my $nntoken = "";
if ($nextchild && $nextchild->next()) { $nntoken = $nextchild->next()->token(); }
if ($nextchild && ($nextchild->token eq "!" || ($enable_javadoc_comments && $nextchild->token eq "*" && $nntoken !~ /^\*/))) {
print STDERR "Found embedded HeaderDoc markup\n" if ($localDebug);
print STDERR "NCT: ".$nextchild->token()."\n" if ($localDebug);
# print STDERR "NCT TREE:\n"; $self->printTree(); print STDERR "NCT ENDTREE\n";
print STDERR "WILL SET SODEC. SEARCHING IN:\n" if ($localDebug);
$self->dbprint() if ($localDebug);
$sodec = $self->nextTokenNoComments($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, 0, $enable_javadoc_comments);
# print STDERR "NCT SODECTREE:\n"; $sodec->printTree(); print STDERR "NCT ENDTREE\n";
print STDERR "SODEC SET TO $sodec\n" if ($localDebug);
if ($sodec) {
$sodec->dbprint() if ($localDebug);
}
my $string = $firstchild->textTree();
my $fullpath = $apio->fullpath();
my $linenum = $apio->linenum();
if ($token eq $parseTokens{soc} && $parseTokens{soc}) {
my $eoc = $parseTokens{eoc};
$string =~ s/\Q$eoc\E\s*$//s;
}
if ($string =~ /^\s*\!/o) {
$string =~ s/^\s*\!//so;
my $tagstring = $string;
$tagstring =~ s/^\s*\@//so;
print STDERR "EOD $eoDeclaration NT $ntoken STR $string\n" if ($localDebug);;
if ((($eoDeclaration && $lastDeclaration =~ /[a-zA-Z]/) || !$ntoken ||
$ntoken =~ /[)}]/o || casecmp($ntoken, $parseTokens{rbrace}, $case_sensitive)) &&
($string !~ /^\s*\@/o || !validTag($tagstring, 0))) {
if (!$inBlockDefine) {
$string =~ s/\@abstract/ /sg;
$string =~ s/\@discussion/ /sg;
}
# If we're at the end of a declaration (prior to the
# following newline) and the declaration starts with
# a string of text (JavaDoc-style markup), we need to
# figure out the name of the previous declaration and
# insert it.
print STDERR "Using previous declaration because:\n" if ($localDebug);
print STDERR "EODEC: $eoDeclaration NTOKEN: \"$ntoken\"\n" if ($localDebug);
print STDERR "RBRACE: \"$parseTokens{rbrace}\" STRING: \"$string\"\n" if ($localDebug);
if (!$eoDeclaration) {
print STDERR "LASTDITCH PROCESSING\n" if ($localDebug);
} else {
print STDERR "EOD PROCESSING\n" if ($localDebug);
}
# Roll back to the previous start of declaration.
# This comment is at the end of a line or whatever.
$nextsodec = $sodec;
$sodec = $lastsodec;
$string =~ s/^\s*//so;
print STDERR "COMMENTSTRING WAS: $string\n" if ($localDebug);
print STDERR "PRE1\n" if ($localDebug);
print STDERR "LAST DECLARATION: $lastDeclaration\n" if ($localDebug);
print STDERR "calling getNameAndFieldTypeFromDeclaration [1]\n" if ($localDebug || $fieldTypeDebug);
my ($name, $type) = $self->getNameAndFieldTypeFromDeclaration($lastDeclaration, $apio, $parseTokens{typedefname}, $case_sensitive, $keywordhashref);
if ($name) {
$string = "$type $name\n$string";
print STDERR "COMMENTSTRING NOW: $string\n" if ($localDebug);
}
} elsif (!$eoDeclaration && (!$ntoken ||
$ntoken =~ /[)]/o || casecmp($ntoken, $parseTokens{rbrace}, $case_sensitive)) &&
$string =~ /^\s*\@/o) {
# We have found an embedded headerdoc comment embedded that is
# right before a close parenthesis, but which starts with an @ sign.
my $nlstring = $string;
$nlstring =~ s/[\n\r]/ /sg;
my $emptyok = emptyHDok($nlstring);
if (!$emptyok) {
warn "$fullpath:$linenum: warning: Found invalid headerdoc markup: $nlstring\n";
$dropinvalid = 1;
} elsif ($emptyok == 1) {
warn "$fullpath:$linenum: warning Found headerdoc markup where none expected: $nlstring\n";
} else {
print STDERR "Found always-empty headerdoc markup at end of block: $nlstring\n" if ($localDebug);
$dropinvalid = 2;
}
}
$string =~ s/^\s*//so;
my $tagstring = $string;
$tagstring =~ s/^\s*\@//so;
# warn("STR: $string TAGSTR: $tagstring\n");
# warn(validTag("", 0)." != ".validTag($tagstring, 0));
if ($string =~ /^\s*\@/o && validTag($tagstring, 0)) { # $string =~ /^\s*\@/o
print STDERR "COMMENTSTRING: $string\n" if ($localDebug);
my $fieldref = stringToFields(doxyTagFilter($string), $fullpath, $linenum, $xmlmode, $apio->lang(), $apio->sublang());
# print STDERR "APIO: $apio\n";
foreach my $owner (@{$apiolist}) {
my $copy = $fieldref;
print STDERR "OWNER[1]: $owner\n" if ($localDebug);
if ($owner) {
if (!$inBlockDefine || $do_process == $owner) {
my @copyarray = @{$copy};
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
if ($inBlockDefine && !length($copyarray[0])) { $copyarray[1] =~ s/^field .*?\n/discussion /s; $copy = \@copyarray; }
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
if ($dropinvalid != 2) {
$owner->processComment($copy, 1, $sodec, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
}
}
}
}
# print STDERR "APIO: $apio\n";
$apio->{APIREFSETUPDONE} = 0;
} else {
if (!$dropinvalid) {
if (!$inBlockDefine) {
$string =~ s/\@abstract/ /sg;
$string =~ s/\@discussion/ /sg;
}
$pendingHDcomment = $string;
# } else {
# warn("DROPPING $string\n");
}
}
if (!$HeaderDoc::dumb_as_dirt) {
# Drop this comment from the output.
if ($xmlmode || ($apio->lang() eq "applescript")) {
# We were doing this for HTML when we needed to
# be able to reparse the tree after copying
# it to a cloned data type. This is no longer
# needed, and the old method (above) is slightly
# faster.
#
# However, we still have to use this approach
# for AppleScript, for that reason.
$self->hidden(1); $skipchildren = 1;
} else {
$self->{TOKEN} = "";
$self->{FIRSTCHILD} = undef;
print STDERR "HIDING $self\n" if ($localDebug);
}
print STDERR "DROP\n" if ($localDebug);
$curDeclaration = $oldCurDeclaration;
}
}
}
}
} elsif ($token =~ /[;,}]/o) {
print STDERR "Punctuation\n" if ($localDebug);
print STDERR "SETTING LASTDEC TO $curDeclaration\n" if ($localDebug);
$lastDeclaration = "$curDeclaration\n";
if ($pendingHDcomment) {
# If we're at the end of a declaration (prior to the
# following newline) and the declaration starts with
# a string of text (JavaDoc-style markup), we need to
# figure out the name of the previous declaration and
# insert it.
print STDERR "PRE2\n" if ($localDebug);
print STDERR "calling getNameAndFieldTypeFromDeclaration [2]\n" if ($localDebug || $fieldTypeDebug);
my ($name, $type) = $self->getNameAndFieldTypeFromDeclaration($lastDeclaration, $apio, $parseTokens{typedefname}, $case_sensitive, $keywordhashref);
if ($name) {
my $string = "$type $name\n$pendingHDcomment";
my $fullpath = $apio->fullpath();
my $linenum = $apio->linenum();
my $fieldref = stringToFields(doxyTagFilter($string), $fullpath, $linenum, $xmlmode, $apio->lang(), $apio->sublang());
print STDERR "COMMENTSTRING: $string\n" if ($localDebug);
print STDERR "OWNERSTART\n" if ($localDebug);
foreach my $owner (@{$apiolist}) {
my $copy = $fieldref;
print STDERR "OWNER[2]: $owner\n" if ($localDebug);
if ($owner) {
if (!$inBlockDefine || $do_process == $owner) {
my @copyarray = @{$copy};
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
if ($inBlockDefine && !length($copyarray[0])) { $copyarray[1] =~ s/^field .*?\n/discussion /s; $copy = \@copyarray; }
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
$owner->processComment($copy, 1, $sodec, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
}
}
}
print STDERR "OWNEREND\n" if ($localDebug);
}
# print STDERR "APIO: $apio\n";
$apio->{APIREFSETUPDONE} = 0;
$pendingHDcomment = "";
} else {
$eoDeclaration = 1;
}
$curDeclaration = "";
} elsif ($token =~ /[\r\n]/o && $lastDeclaration) {
print STDERR "NLCR\n" if ($localDebug);
$lastDeclaration = "";
} elsif ($token !~ /\s/o) {
print STDERR "Whitespace\n" if ($localDebug);
$eoDeclaration = 0;
}
$sodec = $nextsodec;
my $firstchild = $self->firstchild();
my $next = $self->next();
if ($firstchild && !$skipchildren) {
my $nestallowed = commentsNestedIn($token, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $parseTokens{lbrace}, $case_sensitive);
if ($nestallowed == 1) {
my $newcontinue; my $newASterminator;
($eoDeclaration, $pendingHDcomment, $newcontinue, $newASterminator) = $firstchild->processEmbeddedTagsRec($xmlmode, $eoDeclaration, $parseTokensRef, $case_sensitive, $keywordhashref, "", "", "", $apio, $apiolist, $sodec, $lastTreeNode, $enable_javadoc_comments, $ASterminator);
if ($continue) { $continue = $newcontinue; }
if (!$ASterminator) { $ASterminator = $newASterminator; }
} else {
my $newcontinue; my $newASterminator;
($eoDeclaration, $pendingHDcomment, $newcontinue, $newASterminator) = $firstchild->processEmbeddedTagsRec($xmlmode, $eoDeclaration, $parseTokensRef, $case_sensitive, $keywordhashref, "", "$curDeclaration", "", $apio, $apiolist, $sodec, $lastTreeNode, $enable_javadoc_comments, $ASterminator);
if ($continue) { $continue = $newcontinue; }
if (!$ASterminator) { $ASterminator = $newASterminator; }
}
$curDeclaration .= textTree($firstchild);
} elsif ($firstchild && !$skipchildren) {
$curDeclaration .= textTree($firstchild);
}
if ($ntoken) {
print STDERR "NTOKEN: $ntoken\n" if ($localDebug);
} else {
print STDERR "NTOKEN: (null)\n" if ($localDebug);
}
if (!$ntoken || $ntoken =~ /[)]/o || casecmp($ntoken, $parseTokens{rbrace}, $case_sensitive)) {
# Last-ditch chance to process pending comment.
# This takes care of the edge case where some languages
# do not require the last item in a struct/record to be
# terminated by a semicolon or comma.
if ($ntoken =~ /[)}]/o || casecmp($ntoken, $parseTokens{rbrace}, $case_sensitive)) {
if ($oldCurDeclaration =~ /\S/) {
# Don't use curDeclaration here. It may have been cleared already.
$lastDeclaration = $oldCurDeclaration.$token;
print STDERR "CLOSEBRACE LASTDITCH: SETTING LASTDEC TO $lastDeclaration\n" if ($localDebug);
}
} else {
if ($oldCurDeclaration =~ /\S/) {
print STDERR "NONCLOSEBRACE LASTDITCH: SETTING LASTDEC TO $curDeclaration\n" if ($localDebug);
$lastDeclaration = $curDeclaration;
}
}
if ($pendingHDcomment) {
print STDERR "LASTDITCH\n" if ($localDebug);
print STDERR "LAST DECLARATION:\n$lastDeclaration\nEND OF LAST DECLARATION\n" if ($localDebug);
# If we're at the end of a declaration (prior to the
# following newline) and the declaration starts with
# a string of text (JavaDoc-style markup), we need to
# figure out the name of the previous declaration and
# insert it.
print STDERR "PRE3\n" if ($localDebug);
if (!$lastDeclaration) { $lastDeclaration = $curDeclaration; };
print STDERR "calling getNameAndFieldTypeFromDeclaration [3]\n" if ($localDebug || $fieldTypeDebug);
my ($name, $type) = $self->getNameAndFieldTypeFromDeclaration($lastDeclaration, $apio, $parseTokens{typedefname}, $case_sensitive, $keywordhashref);
if ($name) {
my $string = "$type $name\n$pendingHDcomment";
my $fullpath = $apio->fullpath();
my $linenum = $apio->linenum();
my $fieldref = stringToFields(doxyTagFilter($string), $fullpath, $linenum, $xmlmode, $apio->lang(), $apio->sublang());
print STDERR "COMMENTSTRING: $string\n" if ($localDebug);
foreach my $owner (@{$apiolist}) {
my $copy = $fieldref;
print STDERR "OWNER[3]: $owner\n" if ($localDebug);
if ($owner) {
if (!$inBlockDefine || $do_process == $owner) {
my @copyarray = @{$copy};
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
if ($inBlockDefine && !length($copyarray[0])) { $copyarray[1] =~ s/^field .*?\n/discussion /s; $copy = \@copyarray; }
# print STDERR "COPY[1]: ".$copyarray[1]."\n";
$owner->processComment($copy, 1, $sodec, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
}
}
}
}
# print STDERR "APIO: $apio\n";
$apio->{APIREFSETUPDONE} = 0;
$pendingHDcomment = "";
}
}
# $sodec = $oldsodec;
if ($ASterminator == $self) {
$continue = 0;
}
if ($next && $continue) {
($eoDeclaration, $pendingHDcomment, $continue) = $next->processEmbeddedTagsRec($xmlmode, $eoDeclaration, $parseTokensRef, $case_sensitive, $keywordhashref, $lastDeclaration, $curDeclaration, $pendingHDcomment, $apio, $apiolist, $sodec, $lastTreeNode, $enable_javadoc_comments, $ASterminator);
}
return ($eoDeclaration, $pendingHDcomment, $continue, $ASterminator);
}
# THIS CODE USED TO PROCESS COMMENTS WHENEVER IT IS TIME.
# my $fieldref = stringToFields(doxyTagFilter($string), $fullpath, $linenum, $xmlmode, $lang, $sublang);
# $apio->processComment($fieldref, 1, $self, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $self, $hashtreecur, $hashtreeroot);
# $apio->{APIREFSETUPDONE} = 0;
# /*!
# @abstract
# Gets/sets the next parse tree node (sibling) for this one.
# @param self
# This parse tree object.
# @param NEXT
# The new next node. (Optional.)
# */
sub next {
my $self = shift;
if (@_) {
my $node = shift;
$self->{NEXT} = $node;
}
return $self->{NEXT};
}
# /*!
# @abstract
# Gets/sets the first child parse tree node for this one.
# @param self
# This parse tree object.
# @param NEXT
# The new child node. (Optional.)
# */
sub firstchild {
my $self = shift;
if (@_) {
my $node = shift;
$self->{FIRSTCHILD} = $node;
}
return $self->{FIRSTCHILD};
}
# /*!
# @abstract
# Gets/sets the parent parse tree node for this one.
# @param self
# This parse tree object.
# @param NEXT
# The new parent node. (Optional.)
# */
sub parent {
my $self = shift;
if (@_) {
my $node = shift;
$self->{PARENT} = $node;
# weaken($self->{PARENT});
}
return $self->{PARENT};
}
# /*!
# @abstract
# Prints the text of the parse tree to standard output (for debugging).
# @param self
# The top of the tree/subtree to print
# @seealso //apple_ref/perl/instm/HeaderDoc::ParseTree/dbprint//() dbprint
# */
sub printTree {
my $self = shift;
print STDERR "BEGINPRINTTREE\n";
print STDERR $self->textTree();
print STDERR "ENDPRINTTREE\n";
}
# /*!
# @abstract
# Prints a text representation of the parse tree as a string.
# @param self
# The top of the tree/subtree to dump.
# */
sub textTree {
my $self = shift;
my $nohidden = 0;
if (@_) {
$nohidden = shift;
}
my $parserState = $self->parserState();
my $lastnode = undef;
my $localDebug = 0;
if ($parserState) {
$lastnode = $parserState->{lastTreeNode};
}
print STDERR "TEXTTREE: LASTNODE: $lastnode\n" if ($localDebug);
if ($lastnode && $localDebug) { print STDERR "LASTNODE TEXT: \"".$lastnode->token()."\"\n"; }
my ($string, $continue) = $self->textTreeSub(0, $nohidden, "", "", "", $lastnode);
return $string;
}
# /*!
# @abstract
# Prints a text representation of the parse tree as a string with
# all comments omitted.
# @param self
# The top of the tree/subtree to dump.
# @param lang
# The current language.
# @param sublang
# The current language dialect (e.g. cpp for C++).
# */
sub textTreeNC {
my $self = shift;
my $lang = shift;
my $sublang = shift;
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $macronamesref,
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $parseTokens{soc}) = parseTokens($lang, $sublang);
my $nohidden = 0;
if (@_) {
$nohidden = shift;
}
my %parseTokens = %{parseTokens($lang, $sublang)};
my ($string, $continue) = $self->textTreeSub(1, $nohidden, $parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b});
return $string;
}
# /*!
# @abstract
# The recursive portion of {@link textTree} and
# {@link textTreeNC}.
# @param self
# The top of the tree/subtree to dump.
# @param nc
# Set to 1 if comments should be dropped, else 0.
# @param nh
# Set to 1 if hidden tokens should be dropped, else 0.
# @param soc
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param ilc
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param ilc_b
# The start-of-comment token for the current programming language.
# Obtained by a call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param lastnode
# The last node to include. This node and children of this node are
# included, but not siblings of this node.
# */
sub textTreeSub
{
my $self = shift;
my $nc = shift;
my $nh = shift;
my $soc = shift;
my $ilc = shift;
my $ilc_b = shift;
my $lastnode = shift;
my $localDebug = 0;
my $continue = 1;
print STDERR "TTSUB: LN: $lastnode SELF: $self TOK: ".$self->token()."\n" if ($localDebug);
if ($lastnode == $self) {
print STDERR "TTSUB: CONTINUE -> 0\n" if ($localDebug);
$continue = 0;
}
my $string = "";
my $skip = 0;
my $token = $self->token();
if ($nc) {
if ($localDebug) {
print STDERR "NC\n";
print STDERR "SOC: $soc\n";
print STDERR "ILC: $ilc\n";
print STDERR "ILC_B: $ilc_b\n";
print STDERR "TOK: $token\n";
}
if (($token eq "$soc") || ($token eq "$ilc") || ($token eq "$ilc_b")) {
$skip = 1;
}
}
if ($nh) {
if ($self->hidden()) {
$skip = 1;
}
}
if (!$skip) {
$string .= $token;
# if (!$continue) {
# return ($string, $continue);
# }
if ($self->{FIRSTCHILD}) {
my $node = $self->{FIRSTCHILD};
bless($node, "HeaderDoc::ParseTree");
my ($newstring, $newcontinue) = $node->textTreeSub($nc, $nh, $soc, $ilc, $ilc_b, $lastnode);
if ($continue) { $continue = $newcontinue; }
$string .= $newstring;
}
}
if (!$continue) {
return ($string, $continue);
}
if ($self->{NEXT}) {
my $node = $self->{NEXT};
bless($node, "HeaderDoc::ParseTree");
my ($newstring, $newcontinue) = $node->textTreeSub($nc, $nh, $soc, $ilc, $ilc_b, $lastnode);
$continue = $newcontinue;
$string .= $newstring;
}
return ($string, $continue);
}
# /*!
# @abstract
# Returns an XML representation of a parse tree/subtree.
# @param self
# The top of the tree/subtree to print.
# @param keep_whitespace
# Set to 1 if whitespace should be preserved in the
# output, else 0.
# @param drop_pdefine_contents
# Set to 1 if the contents of #define macros should be
# dropped from the output, else 0.
# */
sub xmlTree {
my $self = shift;
my $keep_whitespace = shift;
my $drop_pdefine_contents = shift;
# my $apiOwner = shift;
# my $apio = $self->apiOwner();
if ($self->{XMLTREE}) { return $self->{XMLTREE}; }
# $self->printTree();
# $self->dbprint();
my $apiOwner = undef;
my $lang = undef;
my $sublang = undef;
my $occmethod = 0;
my $localDebug = 0;
my $debugName = ""; # "TypedefdStructWithCallbacksAndStructs";
if ($self->apiOwner()) {
$apiOwner = $self->apiOwner();
bless($apiOwner, "HeaderDoc::HeaderElement");
bless($apiOwner, $apiOwner->class());
$lang = $apiOwner->lang();
$sublang = $apiOwner->sublang();
if (($debugName ne "") && ($apiOwner->name() eq $debugName)) {
$colorDebug = 1;
} else {
$colorDebug = 0;
print STDERR $apiOwner->name()."\n" if ($localDebug);
}
if ($apiOwner->class() eq "HeaderDoc::Method") {
$occmethod = 1;
} else {
$occmethod = 0;
}
# print STDERR "APIOWNERS was type $apiOwner\n";
} else {
warn("WARNING: Could not find API Owner for parse tree $self.\nFaking data structures. Please file a bug.\n");
$lang = "C"; # $HeaderDoc::lang; # Default to C/C?
$sublang = "C"; # $HeaderDoc::sublang;
$apiOwner = HeaderDoc::HeaderElement->new("LANG" => $lang, "SUBLANG" => $sublang);
$apiOwner->lang($lang);
$apiOwner->sublang($sublang);
$occmethod = 0; # guess
}
# colorizer goes here
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $parseTokens{macronames},
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $parseTokens{soc}) = parseTokens($apiOwner->lang(), $apiOwner->sublang());
my %parseTokens = %{parseTokens($apiOwner->lang(), $apiOwner->sublang())};
$self->processEmbeddedTags(1, $apiOwner);
# , $sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $parseTokens{macronames});
my $lastnode = undef;
my $lastDisplayNode = undef;
my $parserState = $self->parserState();
if ($parserState) {
$lastnode = $parserState->{lastTreeNode};
$lastDisplayNode = $parserState->{lastDisplayNode};
}
if ((!$lastDisplayNode) && $self->{lastDisplayNode}) {
$lastDisplayNode = $self->{lastDisplayNode};
}
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $parseTokens{macronames},
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $parseTokens{soc}) = parseTokens($lang, $sublang);
my %parseTokens = %{parseTokens($lang, $sublang)};
# my %pt = ();
# $pt{sotemplate} = $sotemplate;
# $pt{soconstructor} = $sotemplate;
my $ctstate = newCTState( undef, keep_whitespace => $keep_whitespace, apio => $apiOwner, type => "",
depth => 0, inComment => 0, inQuote => 0, inRubyQuote => 0, inObjCMethod => $occmethod,
lastBrace => "", parseTokensRef => \%parseTokens, prespace => "", lang => $lang, sublang => $sublang,
xmlmode => 1, newlen => 0, breakable => 0, inMacro => 0, inEnum => 0, seenEquals => 0,
lastKeyword => "", lastnstoken => "", lastTreeNode => $lastnode, lastTokenType => "",
spaceSinceLastToken => 0, inAttribute => 0, inRaises => 0, inTypeOf => 0,
drop_pdefine_contents => $drop_pdefine_contents, ASinName => 0, afteradvisoryspace => 0,
lastDisplayNode => $lastDisplayNode);
my ($retvalref, $junka, $junkb, $junkc, $junkd, $junke, $lastTokenType, $spaceSinceLastToken, $junk_afteradvisoryspace) = $self->colorTreeSub($ctstate);
my $retval = ${$retvalref};
# my $retval = "";
# $retval = $self->textTree();
# $self->printTree();
$self->{XMLTREE} = $retval;
return $retval;
}
# /*!
# @abstract
# Returns an HTML representation of a parse tree/subtree.
# @param self
# The top of the tree/subtree to print.
# @param keep_whitespace
# Set to 1 if whitespace should be preserved in the
# output, else 0.
# @param drop_pdefine_contents
# Set to 1 if the contents of #define macros should be
# dropped from the output, else 0.
# */
sub htmlTree {
my $self = shift;
my $keep_whitespace = shift;
my $drop_pdefine_contents = shift;
# my $apiOwner = shift;
# print STDERR "TREE\n";
# $self->printTree();
# $self->dbprint();
# print STDERR "ENDTREE\n";
my $apiOwner = undef;
my $lang = undef;
my $sublang = undef;
my $occmethod = 0;
my $localDebug = 0;
my $debugName = ""; # "TypedefdStructWithCallbacksAndStructs";
# $self->dbprint();
if ($self->{HTMLTREE}) {
print STDERR "SHORTCUT\n" if ($localDebug);
return $self->{HTMLTREE};
}
if ($self->apiOwner()) {
$apiOwner = $self->apiOwner();
bless($apiOwner, "HeaderDoc::HeaderElement");
bless($apiOwner, $apiOwner->class());
$lang = $apiOwner->lang();
$sublang = $apiOwner->sublang();
# print STDERR "LANG: $lang APIO: $apiOwner\n";
if (($debugName ne "") && ($apiOwner->name() eq $debugName)) {
$colorDebug = 1;
} else {
$colorDebug = 0;
print STDERR $apiOwner->name()."\n" if ($localDebug);
}
if ($apiOwner->class() eq "HeaderDoc::Method") {
$occmethod = 1;
} else {
$occmethod = 0;
}
print STDERR "APIOWNERS was type $apiOwner\n" if ($localDebug);
} else {
warn("WARNING: Could not find API Owner for parse tree $self.\nFaking data structures. Please file a bug.\n");
$lang = "C"; # $HeaderDoc::lang;
$sublang = "C"; # $HeaderDoc::sublang;
$apiOwner = HeaderDoc::HeaderElement->new("LANG" => $lang, "SUBLANG" => $sublang);
cluck("BT\n");
$apiOwner->apiOwner($HeaderDoc::headerObject);
$apiOwner->lang($lang);
$apiOwner->sublang($sublang);
$occmethod = 0; # guess
}
# colorizer goes here
my $lastnode = undef;
my $lastDisplayNode = undef;
my $parserState = $self->parserState();
if ($parserState) {
$lastnode = $parserState->{lastTreeNode};
$lastDisplayNode = $parserState->{lastDisplayNode};
}
if ((!$lastDisplayNode) && $self->{lastDisplayNode}) {
$lastDisplayNode = $self->{lastDisplayNode};
}
# print STDERR "TN: $self LTN: $lastnode LDN: $lastDisplayNode\n";
# if ($lang eq "shell") {
# $keep_whitespace = 1;
# }
# my ($sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $parseTokens{macronames},
# $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp,
# $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename,
# $functionisbrace, $classisbrace, $parseTokens{lbraceconditionalre}, $parseTokens{lbraceunconditionalre}, $assignmentwithcolon,
# $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $soconstructor) = parseTokens($lang, $sublang);
my %parseTokens = %{parseTokens($lang, $sublang)};
$self->processEmbeddedTags(0, $apiOwner);
# , $sotemplate, $eotemplate, $operator, $parseTokens{soc}, $parseTokens{eoc}, $parseTokens{ilc}, $parseTokens{ilc_b}, $sofunction,
# $parseTokens{soprocedure}, $parseTokens{sopreproc}, $parseTokens{lbrace}, $parseTokens{rbrace}, $unionname, $structname,
# $enumname,
# $typedefname, $varname, $constname, $structisbrace, $parseTokens{macronames});
# my %pt = ();
# $pt{sotemplate} = $sotemplate;
# $pt{soconstructor} = $sotemplate;
my $ctstate = newCTState( undef, keep_whitespace => $keep_whitespace, apio => $apiOwner, type => "",
depth => 0, inComment => 0, inQuote => 0, inRubyQuote => 0, inObjCMethod => $occmethod,
lastBrace => "", parseTokensRef => \%parseTokens, prespace => "", lang => $lang, sublang => $sublang,
xmlmode => 0, newlen => 0, breakable => 0, inMacro => 0, inEnum => 0, seenEquals => 0,
lastKeyword => "", lastnstoken => "", lastTreeNode => $lastnode, lastTokenType => "",
spaceSinceLastToken => 0, inAttribute => 0, inRaises => 0, inTypeOf => 0,
drop_pdefine_contents => $drop_pdefine_contents, ASinName => 0, afteradvisoryspace => 0,
lastDisplayNode => $lastDisplayNode);
my ($retvalref, $junka, $junkb, $junkc, $junkd, $junke, $lastTokenType, $spaceSinceLastToken, $junk_afteradvisoryspace) = $self->colorTreeSub($ctstate);
my $retval = ${$retvalref};
# my $retval = "";
# $retval = $self->textTree();
# $self->printTree();
if ($HeaderDoc::align_columns) {
my @retarr = split(/(\n)/s, $retval);
my $newret = "";
foreach my $line (@retarr) {
my $first = "";
# print STDERR "LINE: $line\n";
if ($line =~ s/^
cpp for C++).
# */
sub isMacro
{
my $self = shift;
my $token = shift;
my $lang = shift;
my $sublang = shift;
if ($lang ne "C" && $lang ne "Csource") { return 0; }
if ($token =~ /^\#\w+/o) { return 1; }
return 0;
}
# /*!
# @abstract Prints the contents of a colorTreeSub state object.
# */
sub dumpCTState
{
my $ctstateref = shift;
my %ctstate = %{$ctstateref};
print STDERR "DUMPING CTSTATE:\n\n";
foreach my $key (keys %ctstate) {
print " $key => ".$ctstate{$key}."\n";
}
print "\n\n";
}
# /*!
# @abstract
# Returns an HTML or XML representation of a parse tree/subtree.
# @param self
# The top of the tree/subtree to print.
# @param ctstate
# The colortree state object. It should contain the parameters
# listed below.
# @param keep_whitespace
# Set to 1 if whitespace should be preserved in the
# output, else 0.
# @param apio
# The owner of the parse tree. This may be an API owner or
# may jut be an ordinary type class instance (e.g. Struct).
#
# This is mostly used for using methods in the HeaderElement
# class and obtaining filenames/line numbers for debugging.
# @param type
# Indicates information about what we're currently parsing.
# Possible values are:
#
# typedefstructrecordunionenumfuncptrhexnumberpastd - pascal type declarationpasvar - pascal variable declarationpasrec - pascal record declarationinRubyQuote to determine which.%{ or %/<<. Prevents coloring
# of the initial token.cpp for C++).
# @param xmlmode
# Set to 1 if you want XML output, 0 for HTML.
# @param newlen
# The length of the current line. Set to 0 on first call.
# @param breakable
# Indicates whether the formatter can break before this token.
# star, string, char,
# comment, preprocessor,
# number, keyword,
# function, var,
# template, type,
# param, or ignore.
#
# On initial call, an empty string.
# @param spaceSinceLastToken
# Indicates that whitespace has been seen since the last non-space
# token. This ensures that required space does not get deleted.
#
# On initial call, 0.
# @param inAttribute
# On initial call, 0. On recursion, set to 1 after
# the attribute keyword (or equivalent) is encountered.
#
# Causes the next word token to be interpreted as ordinary text.
#
# Also prevents the normal line break after the open parenthesis.
# @param inRaises
# On initial call, 0. On recursion, set to 1 after
# the raises keyword (or equivalent) is encountered.
#
# Causes the next word token to be interpreted as a variable (an
# exception) instead of a type.
#
# Also prevents the normal line break after the open parenthesis.
# @param inTypeOf
# On initial call, 0. On recursion, set to 1 after
# the typeof keyword (or equivalent) is encountered.
#
# Causes the next word token to be interpreted as a variable instead
# of a type.
#
# Also prevents the normal line break after the open parenthesis.
# @param drop_pdefine_contents
# Set to 1 if the contents of #define macros should be
# dropped from the output, else 0.
# @param ASinName
# On initial call, set to 0. During the recursive walk of the
# parse tree, this gets set to 1 when the current tree node in
# an AppleScript name.
#
# @var tokenType
# Contains the type of the last token emitted. One of:
# star, string, char,
# comment, preprocessor,
# number, keyword,
# function, var,
# template, type,
# param, or ignore.
#
# @discussion
# This is a helper function shared by {@link htmlTree} and
# {@link xmlTree}. You should not call this directly.
# */
sub colorTreeSub
{
my $self = shift;
my $ctstateref = shift;
my $localDebug = 0;
my $psDebug = 0;
my $treeDebug = 0;
my $dropDebug = 0;
my $tokenDebug = 0;
my $codePathDebug = 0;
my $rubyDebug = 0;
my $tokenPrintDebug = 0;
my $advisorySpaceDebug = 0;
my $continue = 1;
my %ctstate = %{$ctstateref};
my $token = $self->{TOKEN};
my $ntoken = $self->nextpeek();
my $tokenname = $token;
if ($token eq "\n") { $tokenname = "[NEWLINE]"; }
elsif ($token eq "\r") { $tokenname = "[CARRIAGE RETURN]"; }
elsif (!length($token)) { $tokenname = "[EMPTY STRING]"; }
else { $tokenname = "\"".$tokenname."\""; }
my $ntokenname = $ntoken;
if ($ntoken eq "\n") { $ntokenname = "[NEWLINE]"; }
elsif ($ntoken eq "\r") { $ntokenname = "[CARRIAGE RETURN]"; }
elsif (!length($ntoken)) { $ntokenname = "[EMPTY STRING]"; }
else { $ntokenname = "\"".$ntokenname."\""; }
my $lastnstokenname = $ctstate{lastnstoken};
if ($ctstate{lastnstoken} eq "\n") { $lastnstokenname = "[NEWLINE]"; }
elsif ($ctstate{lastnstoken} eq "\r") { $lastnstokenname = "[CARRIAGE RETURN]"; }
elsif (!length($ctstate{lastnstoken})) { $lastnstokenname = "[EMPTY STRING]"; }
else { $lastnstokenname = "\"".$lastnstokenname."\""; }
print STDERR "***** TOKEN: ".$tokenname." *****\n" if ($codePathDebug || $localDebug || $tokenDebug || $tokenPrintDebug);
# print STDERR "IN COLORTREESUB\n";
if ($self == $ctstate{lastTreeNode}) {
print STDERR "Node is last node in tree. Ending after this node.\n" if ($localDebug || $codePathDebug);
$continue = 0;
}
dumpCTState(\%ctstate) if ($localDebug);
print STDERR "NEWLEN: ".$ctstate{newlen}."\n" if ($localDebug);
my %parseTokens = %{$ctstate{parseTokensRef}};
my %macroList = %{$parseTokens{macronames}};
my $oldLastBrace = $ctstate{lastBrace};
my $oldDepth = $ctstate{depth};
my $oldInMacro = $ctstate{inMacro};
my $oldInQuote = $ctstate{inQuote};
my $oldLastKeyword = $ctstate{lastKeyword};
my $oldInComment = $ctstate{inComment} ;
my $oldInAttribute = $ctstate{inAttribute};
my $oldInRaises = $ctstate{inRaises};
my $oldInTypeOf = $ctstate{inTypeOf};
my $dropFP = 0;
# This cache slows things down now that it works....
# if ($self->{CTSUB}) { return (\$self->{CTSTRING}, $self->{CTSUB}); }
my $keep_all_newlines = 0;
print STDERR "DPC: ".$ctstate{drop_pdefine_contents}."\n" if ($localDebug);
if ($ctstate{lang} eq "shell" || $ctstate{sublang} eq "javascript") {
$keep_all_newlines = 1;
} elsif ($ctstate{lang} eq "python") {
$keep_all_newlines = 1;
$ctstate{keep_whitespace} = 1;
}
if ($ctstate{xmlmode} && $localDebug) {
print STDERR "XMLMODE.\n";
}
# foreach my $listitem (keys %macroList) { print STDERR "ML: $listitem\n"; }
print STDERR "IM: ".$ctstate{inMacro}."\n" if ($localDebug);
print STDERR "IAtt: ".$ctstate{inAttribute}."\n" if ($localDebug);
print STDERR "IRai: ".$ctstate{inRaises}."\n" if ($localDebug);
my $mustbreak = 0;
my $nextprespace = "";
my $string = "";
my $tailstring = "";
my $escapetoken = "";
my ($case_sensitive, $keywordhashref) = $ctstate{apio}->keywords();
my $tokennl = 0;
if ($token =~ /^[\r\n]/o) { $tokennl = 1; }
if (($token eq "\t" || $token =~ /^ +$/) && (!$ctstate{keep_whitespace})) { $token = " "; }
if ($ctstate{inQuote} == 3) {
$keep_all_newlines = 1;
$ctstate{keep_whitespace} = 1;
}
if ($ctstate{keep_whitespace}) {
$ctstate{prespace} = "";
$nextprespace = "";
}
if (length($token) && (!$tokennl)) {
if ($ctstate{afteradvisoryspace}) {
# Remove a single space if we're after a newline that
# got converted into a space
print STDERR "OLD AASTOKEN: \"$token\"\n" if ($localDebug || $advisorySpaceDebug || $codePathDebug);
$token =~ s/^ //;
$ctstate{afteradvisoryspace} = 0;
print STDERR "AASTOKEN NOW: \"$token\"\n" if ($localDebug || $advisorySpaceDebug || $codePathDebug);
print STDERR "AAS -> 0\n" if ($localDebug || $advisorySpaceDebug || $codePathDebug);
}
}
print "keep_all_newlines: $keep_all_newlines\n" if ($localDebug);
my $tokenIsKeyword = isKeyword($token, $keywordhashref, $case_sensitive);
print STDERR "TIK: $tokenIsKeyword\n" if ($localDebug);
# my $ctoken = $self->childpeek($soc, $parseTokens{ilc}, $parseTokens{ilc_b});
print STDERR "TK $token\n" if ($colorDebug || $rubyDebug || $colorDebug);
print STDERR "IRQ is ".$ctstate{inRubyQuote}."\n" if ($rubyDebug || $colorDebug);
print STDERR "IQ is ".$ctstate{inQuote}."\n" if ($rubyDebug || $colorDebug);
my $ctoken = $self->childpeeknc($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b});
my $ntokennc = $self->nextpeeknc($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b});
my $nntokennc = $self->nextnextpeeknc($parseTokens{soc}, $parseTokens{ilc}, $parseTokens{ilc_b});
my $tokenType = undef;
my $drop = 0;
my $firstCommentToken = 0;
my $leavingComment = 0;
my $hidden = ($self->hidden() && !$ctstate{xmlmode});
my $isTypeStar = 0;
my $begintr = "";
my $endtr = "";
my $newtd = "";
if (!$ctstate{xmlmode} && $HeaderDoc::align_columns) {
$begintr = "#define
# macro expression.
# @param self
# The parse tree object. (Unused.)
# @param rawvalue
# The expression to interpret.
# @discussion
# Used by {@link getPTvalue}.
#
# Although it should be possible for the
# new code in MacroFilter.pm to be easily
# adapted to do this job, at the time, that
# code did not exist.
# */
sub trygcc
{
my $self = shift;
my $rawvalue = shift;
my $success = 0;
my $value = 0;
my $timestamp = time();
my $localDebug = 0;
# print STDERR "RV: $rawvalue\n";
if (open(GCCFILE, ">/tmp/headerdoc-gcctemp-$timestamp.c")) {
print GCCFILE "#include #define
# macro expression
# @param self
# The parse tree object.
# @discussion
# Although it should be possible for the
# new code in MacroFilter.pm to be easily
# adapted to do this job, at the time, that
# code did not exist.
# */
sub getPTvalue
{
my $self = shift;
my $success = 0;
my $value = 0;
my $localDebug = 0;
my $pos = $self;
while ($pos && ($pos->token() ne "#define")) {
$pos = $pos->next();
}
if (!$pos) {
return($success, $value);
}
$pos = $pos->firstchild();
while ($pos && ($pos->hidden != 3) && ($pos->{HIDEMACROLASTTOKEN} != 2)) {
$pos = $pos->next();
}
if ($pos) {
my $rawvalue = $pos->textTree();
print STDERR "getPTvalue: WE HAVE A WINNER.\n" if ($localDebug);
print STDERR "RAWVALUE IS: $rawvalue\n" if ($localDebug);
($success, $value) = $self->trygcc($rawvalue);
}
return($success, $value);
}
# /*!
# @abstract
# Releases references in an attempt to allow Perl to
# garbage collect this parse tree.
# @param self
# The root of the parse tree to release.
# */
sub dispose
{
my $self = shift;
# Decrement the reference count.
# cluck("Dispose called on parse tree\n");
if ($self->{REFCOUNT} > 0) {
if ($HeaderDoc::debugAllocations) {
print STDERR "Parse tree reference count is ".$self->{REFCOUNT}.". Not disposing.\n";
print STDERR "SELF: ".$self."\n";
print STDERR "FC: ".$self->{FIRSTCHILD}."\n";
print STDERR "NEXT: ".$self->{NEXT}."\n";
print STDERR "PARENT: ".$self->{PARENT}."\n";
print STDERR "APIOLIST: ".$self->{APIOWNERS}."\n";
foreach my $obj (@{$self->{APIOWNERS}}) {
print STDERR " OWNER: $obj\n";
}
print STDERR "END OF LIST\n";
}
return;
}
print STDERR "Disposing of tree\n" if ($HeaderDoc::debugAllocations);
$self->{PARENT} = undef;
if ($self->{FIRSTCHILD}) {
$self->{FIRSTCHILD}->dispose();
$self->{FIRSTCHILD} = undef;
}
if ($self->{NEXT}) {
$self->{NEXT}->dispose();
$self->{NEXT} = undef;
}
$self->{NTNC} = undef;
if ($self->{APIOWNERS}) { $self->{APIOWNERS} = (); }
if ($self->{PARSERSTATE}) { $self->{PARSERSTATE} = undef; }
$self->{PARSEDPARAMS} = ();
$self->{RAWPARSEDPARAMETERS} = ();
$self->{TOKEN} = undef;
$self->dbprint() if ($HeaderDoc::debugAllocations);
}
# /*!
# @abstract
# Called by Perl when a node is garbage collected.
# @param self
# The root of the parse tree to release.
# @discussion
# Used for debugging memory behavior.
# */
sub DESTROY
{
my $self = shift;
print STDERR "Destroying $self\n" if ($HeaderDoc::debugAllocations);
}
# /*!
# @abstract
# Counts the number of curly braces at this level in the parse tree.
# @param self
# This parse tree node.
# @param lbrace
# The left brace token to look for.
# @param last
# The last node to include.
# @discussion
# Because this function checks at a given level,
# it updates the last node to be the ancestor
# of the last node at the same level as the
# starting node. (Why doesn't this use
# {@link nextAtLevelOf}?)
# */
sub curlycount
{
my $self = shift;
my $lbrace = shift;
my $last = shift;
my $pos = $self;
my $count = 0;
while ($last && !$last->isAfter($self)) {
$last = $last->parent();
}
if ($last) { $last = $last->next(); } # first node after this declaration
while ($pos && $pos != $last) {
if ($pos->token eq "$lbrace") {
$count++;
}
$pos = $pos->next();
}
return $count;
}
# /*!
# @abstract
# Walks this parse tree node's sibling chain and
# returns the node after the nth left brace.
# @param self
# The starting parse tree node.
# @param lbrace
# The left brace token to look for.
# @param count
# The number of braces to skip past.
# */
sub skipcurly
{
my $self = shift;
my $lbrace = shift;
my $count = shift;
my $localDebug = 0;
my $pos = $self;
print STDERR "SKIPPING $count curly braces (lbrace = '$lbrace') at POS=$pos\n" if ($localDebug);
if (!$count) { return $self; }
while ($pos) {
my $tok = $pos->token();
print STDERR "TOKEN: '$tok'\n" if ($localDebug);
if ($tok eq "$lbrace") {
print STDERR "MATCH\n" if ($localDebug);
if (!--$count) {
my $next = $pos->next;
if ($localDebug) {
print STDERR "FOUND ONE. Next tree is:\n";
if ($next) {
$next->dbprint();
} else {
print STDERR "UNDEFINED!\n";
}
}
return $next;
}
}
$pos = $pos->next();
}
warn "Yikes! Ran out of open braces!\n";
return $pos;
}
# /*!
# @abstract
# Returns whether this token is a start token
# for a C preprocessor macro.
# @param token
# The token to check.
# @param macroListRef
# A reference to the macro hash returned by a
# call to
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/parseTokens//() parseTokens}.
# @param case_sensitive
# Set to 0/1 depending on whether the language uses
# case-sensitive token matching. This value is
# generally obtained with a call to
# {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/keywords//() keywords}.
# */
sub isKnownMacroToken
{
my $token = shift;
my $macroListRef = shift;
my $case_sensitive = shift;
my %macroList = %{$macroListRef};
if ($case_sensitive) {
if ($macroList{$token}) { return 1; }
return 0;
}
foreach my $cmpToken (keys %macroList) {
if (casecmp($token, $cmpToken, $case_sensitive)) {
return 1;
}
}
return 0;
}
# /*!
# @abstract
# Scrapes out the children of a new-style availability
# macro and returns a sanitized array.
# @param self
# The parse tree node, taken from
# {@link //apple_ref/perl/data/HeaderDoc::ParserState/availabilityNodesArray availabilityNodesArray}
# in the
# {@link //apple_ref/perl/cl/HeaderDoc::ParserState ParserState} object.
# @discussion
# Most of the hard work is done by
# {@link //apple_ref/perl/instm/HeaderDoc::Utilities/complexAvailabilityToArray//() complexAvailabilityToArray}.
#
# This function is exclusively used by
# {@link //apple_ref/perl/instm/HeaderDoc::BlockParse/mergeComplexAvailability//() mergeComplexAvailability}.
# */
sub parseComplexAvailability
{
my $self = shift;
my $localDebug = 0;
print STDERR "parseComplexAvailability: dumping tree for $self.\n" if ($localDebug);
$self->dbprint() if ($localDebug);
my $token = $self->token();
my $availstring = "";
my $pos = $self->firstchild();
while ($pos && ($pos->token() ne "(")) {
$pos = $pos->next();
}
if (!$pos) { my @arr = (); return \@arr; }
$pos = $pos->next();
while ($pos && ($pos->token() ne ")")) {
$availstring .= $pos->token();
$pos = $pos->next();
}
print STDERR "TOKEN: $token\nSTRING: $availstring\n" if ($localDebug);
return complexAvailabilityToArray($token, $availstring);
}
# /*!
# @abstract
# Gets/sets whether this token is an availability macro.
# @param self
# The parse tree node.
# @param newvalue
# The new value to set. (Optional.)
# */
sub isAvailabilityMacro
{
my $self = shift;
if (@_) {
$self->{ISAVAILABILITYMACRO} = shift;
}
if ($self->{ISAVAILABILITYMACRO}) {
return $self->{ISAVAILABILITYMACRO};
}
# my $parent = $self->parent();
# if ($parent) { return $parent->isAvailabilityMacro(); }
return 0;
}
# /*!
# @abstract
# Partially translates Perl code into C
# */
sub translateTree {
my $self = shift;
my %state = ();
$self->translateTreeRec(\%state);
}
# /*!
# @abstract
# Returns the parse tree node containing the
# next non-space token at the current level.
# @param self
# The initial parse tree node.
# */
sub nextNSToken
{
my $self = shift;
my $next = $self->{NEXT};
while ($next && $next->{TOKEN} !~ /\S/s) {
$next = $next->{NEXT};
}
return $next;
}
# /*! @abstract
# Does the real work of translating a Perl script (partially)
# to C.
# @param self
# The tree to translate.
# @param self
# A translation state object (initially empty).
# */
sub translateTreeRec {
my $self = shift;
my $stateref = shift;
my %state = %{$stateref};
my $localDebug = 0;
my $enteringComment = 0;
my $enteringString = 0;
my $enteringSingle = 0;
my $leavingHDComment = 0;
print STDERR "TOK: ".$self->{TOKEN}."\n" if ($localDebug);
if ((!$state{seenCode}) && $self->{TOKEN} && $self->{TOKEN} =~ /\S/s && $self->{TOKEN} ne "if" && $self->{TOKEN} ne "#" && $self->{TOKEN} ne "{" && $self->{TOKEN} ne "}") {
print STDERR "SEENCODE -> 1\n" if ($localDebug);
$state{seenCode} = 1;
$state{insertIfBeforeNode} = $self;
}
print STDERR "CHECK: INSTRING: ".$state{inString}." INCOMMENT: ".$state{inComment}." INREGEXP: ".$state{inRegExp}." INSINGLE: ".$state{inSingle}."\n" if ($localDebug);
if (!($state{inString} || $state{inComment} || $state{inRegExp} || $state{inSingle})) {
# Don't mess with the guts of these things, for the most part.
print STDERR "FIXUP OKAY\n" if ($localDebug);
if ($self->{TOKEN} eq "\"") {
$state{inString} = 1;
$enteringString = 1;
} elsif ($self->{TOKEN} eq "'") {
$state{inSingle} = 1;
$enteringSingle = 1;
} elsif ($self->{TOKEN} eq "#") {
print STDERR "ILC\n" if ($localDebug);
$state{inComment} = 1;
$enteringComment = 1;
$state{socNode} = $self;
if ($state{inHDComment}) {
$self->{TOKEN} = " ";
} else {
$self->{TOKEN} = "//";
}
if ($self->{FIRSTCHILD}->textTree() =~ /\*\//) {
$leavingHDComment = 1;
}
} elsif ($self->{TOKEN} eq "\$") { # Merge with next token.
my $next = $self->{NEXT};
if ($next) {
$self->{TOKEN} = "";
$self->{NEXT}->{TOKEN} = "\$".$self->{NEXT}->{TOKEN};
}
} elsif ($self->{TOKEN} eq ":") {
if ($self->{NEXT} && $self->{NEXT}->{TOKEN} eq ":") {
$self->{TOKEN} = "_";
$self->{NEXT}->{TOKEN} = "_";
}
} elsif ($self->{TOKEN} eq "elsif") {
$self->{TOKEN} = "else if";
} elsif ($self->{TOKEN} eq "STDERR") {
$self->{TOKEN} = "stderr";
} elsif ($self->{TOKEN} eq "STDOUT") {
$self->{TOKEN} = "stdout";
} elsif ($self->{TOKEN} eq "STDIN") {
$self->{TOKEN} = "stdin";
} elsif ($self->{TOKEN} eq "<" && $self->{FIRSTCHILD} &&
$self->{FIRSTCHILD}->{NEXT} &&
$self->{FIRSTCHILD}->{NEXT}->{TOKEN} =~ /^[a-zA-Z]+$/ &&
$self->{FIRSTCHILD}->{NEXT}->{NEXT} &&
$self->{FIRSTCHILD}->{NEXT}->{NEXT}->{TOKEN} eq ">") {
# print STDERR "FC: ".$self->{FIRSTCHILD}->{TOKEN}." NC ".$self->{FIRSTCHILD}->{NEXT}->{TOKEN}."\n";
print STDERR "Read from file.\n" if ($localDebug);
$self->{TOKEN} = "stringFromFP(";
$self->{FIRSTCHILD}->{NEXT}->{NEXT}->{TOKEN} = ")";
} elsif ($self->{TOKEN} eq "print" || $self->{TOKEN} eq "warn") {
my $nextnstoken = $self->nextNSToken();
if ($self->{TOKEN} eq "print") {
my $ns = $self->nextNSToken();
if ($ns && ($ns->{TOKEN} eq "\"" || $ns->{TOKEN} eq "\$")) {
$self->{TOKEN} = "printf";
} else {
$self->{TOKEN} = "fprintf";
if ($ns->{TOKEN} eq "STDERR") {
$ns->{TOKEN} = "stderr";
} elsif ($ns->{TOKEN} eq "STDOUT") {
$ns->{TOKEN} = "stdout";
}
$ns->{TOKEN} .= ",";
}
}
if ($nextnstoken->{TOKEN} ne "(") {
$self->{TOKEN} .= "(";
$state{addParenAtSemi} = 1;
$self->{NEXT} = $nextnstoken;
}
} elsif ($self->{TOKEN} eq "split") {
$self->{TOKEN} = "regexpSplit";
$state{inSplit} = 1;
print STDERR "inSplit -> 1\n" if ($localDebug);
} elsif ($self->{TOKEN} eq "(" && $state{inSplit}) {
$state{inSplit} = 2;
print STDERR "inSplit -> 1\n" if ($localDebug);
} elsif ($self->{TOKEN} eq "->") {
print STDERR "->" if ($localDebug);
my $pos = $self->{NEXT};
while ($pos && $pos->{TOKEN} !~ /\S/s) {
$pos = $pos->{NEXT};
}
if ($pos && $pos->{TOKEN} eq "{") {
my $posb = $pos->{FIRSTCHILD};
while ($posb && $posb->{TOKEN} ne "}") {
$posb = $posb->{NEXT};
}
if ($pos && $posb) {
$pos->{TOKEN} = "";
$posb->{TOKEN} = "";
}
}
} elsif ($self->{TOKEN} eq "eq" || $self->{TOKEN} eq "ne") {
my $prev = $self->slowprev();
while ($prev && ($prev->token !~ /\S/s)) {
$prev = $prev->slowprev();
}
while ($prev && $prev->{TOKEN} !~ /\&/ && $prev->{TOKEN} !~/\|/) {
print STDERR "PREV: $prev\n" if ($localDebug);
print STDERR "TOK: ".$prev->{TOKEN}."\n" if ($localDebug);
$prev = $prev->slowprev();
}
# })
# If we run off the beginning of the chain, we hit an open parenthesis,
# so we know we can always insert after the first (always-empty)
# child.
my $addspace = "";
if (!$prev) {
$prev = $self->parent()->firstchild();
} else {
# Add a space after || or &&
$addspace = " ";
}
my $next = $self->{NEXT};
while ($next && ($next->token !~ /\S/s)) {
$next = $next->{NEXT};
}
print STDERR "NEXT IS \"".$next->{TOKEN}."\"\n" if ($localDebug);
while ($next && $next->{NEXT} && $next->{NEXT}->{TOKEN} !~ /\&/ && $next->{NEXT}->{TOKEN} !~ /\|/ &&
$next->{NEXT}->{TOKEN} !~ /\)/) {
$next = $next->{NEXT};
print STDERR "INLOOP NEXT IS ".$next->{TOKEN}."\n" if ($localDebug);
}
if ($prev && $next) {
my $newnode = HeaderDoc::ParseTree->new();
if ($self->{TOKEN} eq "eq") {
$newnode->{TOKEN} = $addspace."!strcmp(";
} else {
$newnode->{TOKEN} = $addspace."strcmp(";
}
$newnode->{NEXT} = $prev->{NEXT};
$prev->{NEXT} = $newnode;
if ($next->{TOKEN} eq "\$") {
$next->{TOKEN} = "";
$next->{NEXT}->{TOKEN} = "\$".$next->{NEXT}->{TOKEN};
$next = $next->{NEXT};
}
$newnode = HeaderDoc::ParseTree->new();
$newnode->{TOKEN} = ")";
$newnode->{NEXT} = $next->{NEXT};
$next->{NEXT} = $newnode;
$self->{TOKEN} = ",";
$prev = $self->slowprev();
while ($prev && ($prev->{TOKEN} =~ /\s/s)) {
$prev->{TOKEN} = "";
$prev = $prev->slowprev();
}
} else {
warn("Could not fix \"eq\" because previous/next token not found (PREV: $prev NEXT: $next).\n");
}
} elsif ($state{seenCode} && ($self->{TOKEN} eq ";" || $self->{TOKEN} eq "{")) {
$state{seenCode} = 0;
if ($state{addParenAtSemi}) {
$state{addParenAtSemi} = 0;
$self->{TOKEN} = ")".$self->{TOKEN};
}
print STDERR "SEENCODE -> 0\n" if ($localDebug);
if ($state{captureIf}) {
my $lastcapture = $self->slowprev();
my $beforenode = $state{insertIfBeforeNode};
my $prev = $beforenode->slowprev();
if (!$prev) {
my $newnode = HeaderDoc::ParseTree->new();
$newnode->{TOKEN} = $beforenode->{TOKEN};
$newnode->{NEXT} = $beforenode->{NEXT};
$beforenode->{NEXT} = $newnode;
$beforenode->{TOKEN} = "";
$prev = $beforenode;
$beforenode = $newnode;
}
my $trailingspace = $self->slowprev();
if (!$trailingspace) {
$trailingspace = $self->parent();
}
while ($trailingspace &&
((!$trailingspace->{TOKEN}) ||
($trailingspace->{TOKEN} =~ /\s/s))) {
# print STDERR "TS: $trailingspace\n" if ($localDebug);
# print STDERR "PARENT: ".$trailingspace->parent()."\n" if ($localDebug);
$trailingspace->{TOKEN} = "";
my $temp = $trailingspace->slowprev();
if ($temp) { $trailingspace = $temp; }
else { $trailingspace = $trailingspace->parent(); }
}
my $copiedDataNode = HeaderDoc::ParseTree->new();
$copiedDataNode->{NEXT} = $prev->{NEXT};
$prev->{NEXT} = $copiedDataNode;
$copiedDataNode->{TOKEN} = $state{capturedText}." ";
print STDERR "ACCUM: ".$state{capturedText}."\n" if ($localDebug);
$state{captureIf} = 0;
print STDERR "captureIf -> 0\n" if ($localDebug);
}
} elsif ($self->{TOKEN} eq "if" && $state{seenCode}) {
$state{capturedText} = "";
$state{captureIf} = 1;
print STDERR "captureIf -> 1\n" if ($localDebug);
}
} elsif ($state{inComment} == 1 && ($self->{TOKEN} eq "/*!" || $self->{TOKEN} eq "/**")) {
$state{socNode}->{TOKEN} = " ";
$state{inHDComment} = 1;
} elsif ($state{inComment} && $self->{TOKEN} =~ /\S/) {
$state{inComment} = 2;
}
print STDERR "RE_STATE: ".$self->{RE_STATE}."\n" if ($localDebug);
if ($self->{RE_STATE} eq "RE_PREFIX") {
print STDERR "Regexp prefix found.\n" if ($localDebug);
if ($self->{TOKEN} eq "s") {
$state{inRegExp} = 3;
$state{rePrefix} = $self->{TOKEN};
$self->{TOKEN} = "";
} elsif ($self->{TOKEN} eq "tr") {
$state{inRegExp} = 4;
$state{rePrefix} = $self->{TOKEN};
$self->{TOKEN} = "";
} elsif ($self->{TOKEN} eq "m") {
$self->{TOKEN} = "";
print STDERR "Dropping prefix because 'm' is equivalent to no prefix.\n" if ($localDebug);
} else {
print STDERR "Unsupported prefix. Ignoring this expression.\n" if ($localDebug);
$state{inRegExp} = -1; # No 'tr' support yet.
}
} elsif ($self->{RE_STATE} eq "RE_PARTSEP") {
if ($self->{NEXT}->{RE_STATE} eq "RE_PARTSEP") {
$self->{NEXT}->{TOKEN} = "";
$self->{NEXT}->{RE_STATE} = "";
}
$self->{TOKEN} = "\", \"";
} elsif ($self->{RE_STATE} eq "RE_START" && ((!$state{inRegExp}) || $state{inRegExp} == 3 || $state{inRegExp} == 4)) {
print STDERR "Regexp start found. INSPLIT: ".$state{inSplit}."\n" if ($localDebug);
$state{regexpName} = $state{prevTokenNode}->{TOKEN};
print STDERR "PTN: ".$state{prevTokenNode}." (".$state{prevTokenNode}->{TOKEN}.") REGEXPNAME: ".$state{regexpName}."\n" if ($localDebug);
if ($state{inSplit}) {
$self->{TOKEN} = "\"";
$state{inRegExp} = 2;
} elsif ($state{regexpName} =~ /^\$[0-9A-Za-z_]+$/) {
if (!$state{inRegExp}) {
$state{inRegExp} = 1;
$state{prevTokenNode}->{TOKEN} = "regexpMatch(".$state{regexpName}.", ";
} elsif ($state{inRegExp} == 3) {
$state{prevTokenNode}->{TOKEN} = "regexpReplace(".$state{regexpName}.", \@\@\@DEST\@\@\@, ";
} elsif ($state{inRegExp} == 4) {
$state{prevTokenNode}->{TOKEN} = "regexptr(".$state{regexpName}.", \@\@\@DEST\@\@\@, ";
}
my $pos = $state{prevTokenNode}->{NEXT};
print STDERR "Deleting the =~ .\n" if ($localDebug);
while ($pos && !$pos->{RE_STATE}) {
print STDERR "Deleting \"".$pos->{TOKEN}."\" ($pos)\n" if ($localDebug);
# Nuke the "=~"
if ($pos->{TOKEN} eq "!") {
$state{prevTokenNode}->{TOKEN} = "!".$state{prevTokenNode}->{TOKEN};
}
$pos->{TOKEN} = "";
$pos = $pos->{NEXT}
}
if ($state{rePrefix} eq "s") {
$self->{TOKEN} = "\"";
# } elsif ($state{rePrefix} eq "tr") {
# $self->{TOKEN} = "\"[";
} else {
$self->{TOKEN} = "\"";
}
} else {
$state{inRegExp} = -1;
}
} elsif ($self->{RE_STATE} eq "RE_END") {
print STDERR "End of regexp found.\n" if ($localDebug);
# Defaults.
my $multiline = 0;
my $insensitive = 0;
my $global = 0;
my $complement = 0;
my $delete = 0;
my $nondestructive = 0;
my $squash = 0;
my $next = $self->{NEXT};
if ($next && $next->{TOKEN} =~ /^[siogmcdr]*$/) {
my $re_trailer = $next->{TOKEN};
if ($re_trailer =~ /s/) {
$multiline = 0; # for 's'
$squash = 1; # for 'tr'
} elsif ($re_trailer =~ /m/) {
$multiline = 1;
}
if ($re_trailer =~ /g/) {
$global = 1;
}
if ($re_trailer =~ /i/) {
$insensitive = 1;
}
if ($re_trailer =~ /c/) {
$complement = 1;
}
if ($re_trailer =~ /d/) {
$delete = 1;
}
if ($re_trailer =~ /r/) {
$nondestructive = 1;
}
$next->{TOKEN} = "";
}
if ($state{inRegExp} == 1) {
$self->{TOKEN} = "\", $multiline, $insensitive, NULL)";
} elsif ($state{inRegExp} == 2) {
# Leave parentheses open.
$self->{TOKEN} = "\"";
my $next = $self->nextNSToken();
print STDERR "THISNEXT: ".$next->{TOKEN}."\n" if ($localDebug);
if (!$next) {
die("Malformed split\n");
}
# $next should be the comma after the regular expression.
$next = $next->nextNSToken();
if ($next->{TOKEN} eq "\$") {
$next->{TOKEN} = "";
$next->{NEXT}->{TOKEN} = "\$".$next->{NEXT}->{TOKEN};
$next = $next->{NEXT};
}
# $next should be the data to split.
my $argNode = HeaderDoc::ParseTree->new();
$argNode->{NEXT} = $next->{NEXT};
$next->{NEXT} = $argNode;
$argNode->{TOKEN} = ", $multiline, $insensitive";
my $afternext = $argNode->nextNSToken();
print STDERR "AN: ".$afternext->{TOKEN}."\n" if ($localDebug);
if (!$afternext || ($afternext->{TOKEN} ne ",")) {
$argNode->{TOKEN} .= ", 0";
}
} elsif ($state{inRegExp} == 3) {
if ($state{rePrefix} eq "s") {
$self->{TOKEN} = "\", $multiline, $insensitive, $global)";
# } elsif ($state{rePrefix} eq "tr") {
# $self->{TOKEN} = "]\"";
} else {
die("Unknown RE prefix \"".$state{rePrefix}."\" [1]\n");
}
} elsif ($state{inRegExp} == 4) {
if ($state{rePrefix} eq "tr") {
# Allowed: "s", but not "m", not "i", not "g"
$self->{TOKEN} = "\", $squash, $complement, $delete)";
if ($nondestructive) {
$state{prevTokenNode}->{TOKEN} = "regexptr(".$state{regexpName}.", NULL, ";
}
} else {
die("Unknown RE prefix \"".$state{rePrefix}."\" [2]\n");
}
}
$state{inRegExp} = 0;
$state{rePrefix} = "";
} elsif ($state{inRegExp}) {
$self->{TOKEN} =~ s/\\/\\\\/g;
}
if ($state{inComment} && $self->{TOKEN} =~ /[\n\r]/) {
print STDERR "Leaving comment.\n" if ($localDebug);
$state{inComment} = 0;
}
print STDERR "Checking token.\n" if ($localDebug);
if ($self->{TOKEN} =~ /^\$[a-zA-Z0-9_]+$/) {
print STDERR "setting PTN\n" if ($localDebug);
$state{prevTokenNode} = $self;
}
if ($state{captureIf}) {
print STDERR "APPEND ".$self->{TOKEN}."\n" if ($localDebug);
$state{capturedText} .= $self->{TOKEN};
$self->{TOKEN} = "";
}
my $resetSplit = 0;
if ($state{inSplit} == 2) {
$resetSplit = 1;
$state{inSplit} = 3;
}
if ($self->{FIRSTCHILD}) {
print STDERR "Processing children.\n" if ($localDebug);
my $tempstateref = $self->{FIRSTCHILD}->translateTreeRec(\%state);
my %tempstate = %{$tempstateref};
if ($state{inComment} && $tempstate{inHDComment}) {
$state{inHDComment} = 1;
}
if ($state{captureIf}) {
$state{capturedText} = $tempstate{capturedText};
}
$state{seenCode} = $tempstate{seenCode};
$state{prevTokenNode} = $tempstate{prevtoken};
}
if ($resetSplit) { $state{inSplit} = 0; print STDERR "inSplit -> 0\n" if ($localDebug) };
if ($enteringComment) { $state{inComment} = 0; $state{seenCode} = 0; }
if ($enteringString) { $state{inString} = 0; }
if ($enteringSingle) { $state{inSingle} = 0; }
if ($leavingHDComment) { $state{inHDComment} = 0; $state{seenCode} = 0; }
if ($self->{NEXT}) {
print STDERR "Processing next.\n" if ($localDebug);
$stateref = $self->{NEXT}->translateTreeRec(\%state);
%state = %{$stateref};
}
print STDERR "Returning.\n" if ($localDebug);
return \%state;
}
# /*!
# @abstract Returns the first token inside the body of an AppleScript
# function.
# */
sub ASFunctionBodyStart
{
my $self = shift;
my $localDebug = 0;
while ($self &&
($self->token() ne "\n") &&
($self->token() ne "\r") &&
($self->token() ne "(")) {
print STDERR "SKIPPING ".$self->token()." (LOOP 1)\n" if ($localDebug);
$self = $self->next();
}
while ($self &&
($self->token() eq "\n") ||
($self->token() eq "\r") ||
($self->token() eq "(")) {
print STDERR "SKIPPING ".$self->token()." (LOOP 2)\n" if ($localDebug);
$self = $self->next();
}
print STDERR "RETURNING TREE.\n" if ($localDebug);
$self->dbprint() if ($localDebug);
return $self;
}
1;