texinfo-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

texinfo/tp Changes MANIFEST Makefile.PL README ...


From: Patrice Dumas
Subject: texinfo/tp Changes MANIFEST Makefile.PL README ...
Date: Mon, 20 Sep 2010 17:19:05 +0000

CVSROOT:        /sources/texinfo
Module name:    texinfo
Changes by:     Patrice Dumas <pertusus>        10/09/20 17:19:05

Added files:
        tp             : Changes MANIFEST Makefile.PL README TODO 
        tp/Texinfo     : Parser.pm 
        tp/t           : 01use.t 02coverage.t 06columnfractions.t 
                         manual_tree.pl 

Log message:
        Add an unfinished texinfo parser module to parse texinfo code into
        a tree.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/Changes?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/MANIFEST?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/Makefile.PL?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/README?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/TODO?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/Texinfo/Parser.pm?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/t/01use.t?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/t/02coverage.t?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/t/06columnfractions.t?cvsroot=texinfo&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/texinfo/tp/t/manual_tree.pl?cvsroot=texinfo&rev=1.1

Patches:
Index: Changes
===================================================================
RCS file: Changes
diff -N Changes
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Changes     20 Sep 2010 17:19:04 -0000      1.1
@@ -0,0 +1,6 @@
+Revision history for Perl extension Texinfo::Parser.
+
+0.01  Sat Sep  4 13:51:26 2010
+       - original version; created by h2xs 1.23 with options
+               -A -X -c -b 5.4.5 -n Texinfo::Parser
+

Index: MANIFEST
===================================================================
RCS file: MANIFEST
diff -N MANIFEST
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ MANIFEST    20 Sep 2010 17:19:04 -0000      1.1
@@ -0,0 +1,6 @@
+Changes
+Makefile.PL
+MANIFEST
+README
+t/Texinfo-Parser.t
+Texinfo/Parser.pm

Index: Makefile.PL
===================================================================
RCS file: Makefile.PL
diff -N Makefile.PL
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Makefile.PL 20 Sep 2010 17:19:04 -0000      1.1
@@ -0,0 +1,12 @@
+use 5.00405;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    NAME              => 'Texinfo::Parser',
+    VERSION_FROM      => 'Texinfo/Parser.pm', # finds $VERSION
+    PREREQ_PM         => {}, # e.g., Module::Name => 1.1
+    ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
+      (ABSTRACT_FROM  => 'Texinfo/Parser.pm', # retrieve abstract from module
+       AUTHOR         => 'Patrice Dumas <address@hidden>') : ()),
+);

Index: README
===================================================================
RCS file: README
diff -N README
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ README      20 Sep 2010 17:19:04 -0000      1.1
@@ -0,0 +1,38 @@
+Texinfo-Parser version 0.01
+===========================
+
+Texinfo::Parser is a perl module for parsing Texinfo code into a 
+tree representing the Texinfo code structure.
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+DEPENDENCIES
+
+This module requires these other modules and libraries:
+
+  blah blah blah
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 
+2010 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.

Index: TODO
===================================================================
RCS file: TODO
diff -N TODO
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ TODO        20 Sep 2010 17:19:04 -0000      1.1
@@ -0,0 +1,4 @@
+In a first step, close when the the command to be closed is at the 
+top of the tree, not when it should be closed theoretically.
+Then it could be possible to come back to the place where the command
+should be closed and give an error message.

Index: Texinfo/Parser.pm
===================================================================
RCS file: Texinfo/Parser.pm
diff -N Texinfo/Parser.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Texinfo/Parser.pm   20 Sep 2010 17:19:05 -0000      1.1
@@ -0,0 +1,1235 @@
+# Parser.pm: parse texinfo code into a tree.
+#
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+# 2009, 2010 Free Software Foundation, Inc.
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License,
+# or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# 
+# Original author: Patrice Dumas <address@hidden>
+# Parts come from texi2html.pl or texi2html.init.
+
+
+package Texinfo::Parser;
+
+use 5.00405;
+use Data::Dumper;
+use strict;
+
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
address@hidden = qw(Exporter);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration      use Texinfo::Parser ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+%EXPORT_TAGS = ( 'all' => [ qw(
+  parser
+  tree_to_texi      
+  parse_texi_text
+  parse_texi_line
+) ] );
+
address@hidden = ( @{ $EXPORT_TAGS{'all'} } );
+
address@hidden = qw(
+);
+
+$VERSION = '0.01';
+
+# i18n
+sub N__($)
+{
+    return $_[0];
+}
+
+sub __($$)
+{
+    my $parser = shift;
+    return &{$parser->{'gettext'}}(@_);
+}
+
+my %default_configuration = (
+  'error' => 'generate',
+  'force' => 0,
+  'no_warn' => 0,
+  'error_limit' => 100,
+  'test' => 0,
+  'debug' => 0,
+  'gettext' => sub {return $_[0];},
+  'aliases' => {},
+  'indices' => []
+);
+
+my %no_brace_commands;             # commands never taking braces
+
+foreach my $no_brace_command ('*',' ',"\t","\n",'-', '|', '/',':','!', 
'?','.','@','}','{',)
+{
+  $no_brace_commands{$no_brace_command} = 1;
+}
+
+# command with braces. value is the max number of arguments.
+my %brace_commands;    
+
+# accent commands. They may be called with and without braces.
+my %accent_commands;
+
+foreach my $no_arg_command 
('TeX','LaTeX','bullet','copyright','registeredsymbol','dots','enddots','equiv','error','expansion','arrow','minus','point','print','result','today','aa','AA','ae','oe','AE','OE','o','O','ss','l','L','DH','dh','TH','th','exclamdown','questiondown','pounds','ordf','ordm','comma','euro','geq','leq','tie','textdegree','quotedblleft','quotedblright','quoteleft','quoteright','quotedblbase','quotesinglbase','guillemetleft','guillemetright','guillemotleft','guillemotright','guilsinglleft','guilsinglright')
+{
+  $brace_commands{$no_arg_command} = 0;
+}
+
+foreach my $accent_command ('"','~','^','`',"'",',','=')
+{
+  $accent_commands{$accent_command} = 0;
+}
+
+foreach my 
$accent_command('ringaccent','H','dotaccent','u','ubaraccent','udotaccent','v','ogonek')
+{
+  $accent_commands{$accent_command} = 1;
+}
+
+foreach my $one_arg_command 
('asis','b','cite','clicksequence','click','code','command','ctrl','dfn','dmn','emph','env','file','headitemfont','i','slanted','sansserif','kbd','key','math','option','r','samp','sc','strong','t','indicateurl','var','verb','titlefont','w','hyphenation','anchor','footnote','shortcaption','caption','dotless')
+{
+  $brace_commands{$one_arg_command} = 1;
+}
+
+foreach my $two_arg_command('email','acronym','abbr')
+{
+  $brace_commands{$two_arg_command} = 2;
+}
+
+foreach my $three_arg_command('uref','url','inforef')
+{
+  $brace_commands{$three_arg_command} = 3;
+}
+
+foreach my $five_arg_command('xref','ref','pxref','inforef','image')
+{
+  $brace_commands{$five_arg_command} = 5;
+}
+
+# commands delimiting blocks, typically with an @end.
+# Value is either the number of arguments on the line separated by
+# commas or the type of command, 'raw', 'bracketed' or 'multitable'.
+my %block_commands;
+foreach my $bracketed_line_argument_command(#'multitable',
+    'deffn',
+    'defvr',
+    'deftypefn',
+    'deftypeop',
+    'deftypevr',
+    'defcv',
+    'deftypecv',
+    'defop',
+    'deftp',
+    'defun',
+    'defmac',
+    'defspec',
+    'defvar',
+    'defopt',
+    'deftypefun',
+    'deftypevar',
+    'defivar',
+    'deftypeivar',
+    'defmethod',
+    'deftypemethod'
+)
+{
+  $block_commands{$bracketed_line_argument_command} = 'bracketed';
+  $block_commands{$bracketed_line_argument_command.'x'} = 'bracketed';
+}
+
+$block_commands{'multitable'} = 'multitable';
+
+foreach my $block_command(
+  'menu', 'detailmenu', 'direntry',
+  'cartouche', 'group', 'raggedright', 'flushleft', 'flushright',
+  'titlepage', 'copying', 'documentdescription',
+  'example', 'smallexample', 'display', 'smalldisplay', 'lisp', 'smalllisp', 
'format', 'smallformat')
+{
+  $block_commands{$block_command} = 0;
+}
+
+# macro is special
+foreach my $raw_command ('html', 'tex', 'xml', 'docbook', 'verbatim', 
'ignore', 'macro')
+{
+  $block_commands{$raw_command} = 'raw';
+}
+
+foreach my $block_command_one_arg('table', 'ftable', 'vtable', 'itemize', 
'enumerate',
+  'quotation', 'small_quotation') # 'macro' ?
+{
+  $block_commands{$block_command_one_arg} = 1;
+}
+
+$block_commands{'float'} = 2;
+
+my %deprecated_commands = (
+  'ctrl' => '',
+  'allow-recursion' => N__('recursion is always allowed'),
+  'quote-arg' => N__('arguments are quoted by default'),
+);
+
+my %forbidden_index_name = ();
+my @default_index_names;
+
+my %index_names =
+(
+ 'cp' => { 'prefixes' => {'cp' => 0,'c' => 0}},
+ 'fn' => { 'prefixes' => {'fn' => 1, 'f' => 1}},
+ 'vr' => { 'prefixes' => {'vr' => 1, 'v' => 1}},
+ 'ky' => { 'prefixes' => {'ky' => 1, 'k' => 1}},
+ 'pg' => { 'prefixes' => {'pg' => 1, 'p' => 1}},
+ 'tp' => { 'prefixes' => {'tp' => 1, 't' => 1}}
+);
+
+foreach my $name(keys(%index_names))
+{
+    foreach my $prefix (keys %{$index_names{$name}->{'prefixes'}})
+    {
+        $forbidden_index_name{$prefix} = 1;
+        push @default_index_names, $name;
+    }
+}
+
+foreach my $other_forbidden_index_name ('info','ps','pdf','htm',
+   'log','aux','dvi','texi','txi','texinfo','tex','bib')
+{
+    $forbidden_index_name{$other_forbidden_index_name} = 1;
+}
+
+my %misc_commands = (
+        'node' => {'arg' => 'line'}, # special arg
+        'bye' => {'skip' => 'line'}, # no arg
+        # set, clear
+        'set' => {'arg' => 'lineraw'}, # special arg
+        'clear' => {'arg' => 1, 'skip' => 'line'}, # special arg
+        # comments
+        'comment' => {'arg' => 'lineraw'},
+        'c' => {'arg' => 'lineraw'},
+        # special
+        'definfoenclose' => {'arg' => 'special'},
+        'alias' => {'args' => 'special'}, 
+        # file names
+        'setfilename' => {'arg' => 'line'},
+        'verbatiminclude'=> {'arg' => 'line'},
+
+        'raisesections' => {'skip' => 'line'},  # no arg
+        'lowersections' => {'skip' => 'line'}, # no arg
+        'contents' => {}, # no arg
+        'shortcontents' => {}, # no arg
+        'summarycontents'=> {}, # no arg
+        'insertcopying'=> {}, # no arg
+        'clickstyle' => {'arg' => 1}, # arg should be an @-command
+        # more relevant in preamble
+        'documentencoding' => {'arg' => 1, 'skip' => 'line'},
+        'setcontentsaftertitlepage' => {}, # no arg
+        'setshortcontentsaftertitlepage' => {}, # no arg
+        'novalidate' => {}, # no arg
+        'dircategory'=> {'arg' => 'line'}, # line. Position with regard 
+                         # with direntry is significant
+        'pagesizes' => {'skip' => 'line', 'arg' => 'line'}, # can have 2 args 
+                                 # or one? 200mm,150mm 11.5in
+        'finalout' => {'skip' => 'line'}, # no arg
+        'paragraphindent' => {'skip' => 'line', 'arg' => 1}, # arg none asis 
+                             # or a number and forbids anything else on the 
line
+        'firstparagraphindent' => {'skip' => 'line', 'arg' => 1}, # none insert
+        'frenchspacing' => {'arg' => 1, 'skip' => 'line'}, # on off
+                                       # not so sure about 'skip' => 'line'
+        'fonttextsize' => {'arg' => 1}, # 10 11
+        'allowcodebreaks' => {'arg' => 1, 'skip' => 'line'}, # false or true
+        'exampleindent' => {'skip' => 'line', 'arg' => 1}, # asis or a number
+        'footnotestyle'=> {'skip' => 'line', 'arg' => 1}, # end and separate
+                                 # and nothing else on the line
+        'afourpaper' => {'skip' => 'line'}, # no arg
+        'afivepaper' => {'skip' => 'line'}, # no arg
+        'afourlatex' => {'skip' => 'line'}, # no arg
+        'afourwide' => {'skip' => 'line'}, # no arg
+        'headings'=> {'skip' => 'line', 'arg' => 1},
+                    #off on single double singleafter doubleafter
+                    # interacts with setchapternewpage
+        'setchapternewpage' => {'skip' => 'line', 'arg' => 1}, # off on odd
+        'everyheading' => {'arg' => 'line'}, # @*heading @*footing use @|
+        'everyfooting' => {'arg' => 'line'}, # + @thispage @thissectionname 
@thissectionnum
+        'evenheading' => {'arg' => 'line'},  # @thissection @thischaptername 
@thischapternum 
+        'evenfooting' => {'arg' => 'line'},  # @thischapter @thistitle 
@thisfile
+        'oddheading' => {'arg' => 'line'},
+        'oddfooting' => {'arg' => 'line'},
+        'smallbook' => {'skip' => 'line'}, # no arg
+        'syncodeindex' => {'skip' => 'line', 'arg' => 2},
+                          # args are index identifiers
+        'synindex' => {'skip' => 'line', 'arg' => 2},
+        'defindex' => {'skip' => 'line', 'arg' => 'special'}, # one identifier 
arg
+        'defcodeindex' => {'skip' => 'line', 'arg' => 'special'}, # one 
identifier arg
+        'documentlanguage' => {'skip' => 'line', 'arg' => 1},
+                                                       # language code arg
+        'kbdinputstyle' => {'skip' => 'whitespace', 'arg' => 1}, # code 
+                                                        #example distinct
+        'everyheadingmarks' => {'skip' => 'line', 'arg' => 1}, # top bottom
+        'everyfootingmarks' => {'skip' => 'whitespace', 'arg' => 1},
+        'evenheadingmarks' => {'skip' => 'whitespace', 'arg' => 1},
+        'oddheadingmarks' => {'skip' => 'whitespace', 'arg' => 1},
+        'evenfootingmarks' => {'skip' => 'whitespace', 'arg' => 1},
+        'oddfootingmarks' => {'skip' => 'whitespace', 'arg' => 1},
+        # not valid for info (should be in @iftex)
+        'cropmarks' => {}, # no arg
+
+        # formatting
+        'center' => {'arg' => 'line'},
+        'printindex' => {'arg' => 1, 'skip' => 'line'},
+        'listoffloats' => {'arg' => 'line'},
+        # especially in titlepage
+        'shorttitle' => {'arg' => 'line'},
+        'shorttitlepage' => {'arg' => 'line'},
+        'settitle' => {'arg' => 'line'},
+        'author' => {'arg' => 'line'},
+        'subtitle' => {'arg' => 'line'},
+        'title' => {'arg' => 'line'},
+        'sp' => {'skip' => 'line', 'arg' => 1}, # no arg 
+                                    # at the end of line or a numerical arg
+        'page' => {}, # no arg (pagebreak)
+        'need' => {'skip' => 'line', 'arg' => 1}, # one numerical/real arg
+        # formatting
+        'noindent' => {'skip' => 'whitespace'}, # no arg
+        'indent' => {'skip' => 'whitespace'},
+        'exdent' => {'skip' => 'space'},
+        'headitem' => {'skip' => 'space'},
+        'item' => {'skip' => 'space'}, # or line, depending on the context
+        'itemx' => {'skip' => 'space'},
+        'tab' => {'skip' => 'space'}, 
+        # not valid for info (should be in @iftex)
+        'vskip' => {'arg' => 'lineraw'}, # arg line in TeX
+        # obsolete @-commands.
+        'refill' => {}, # no arg (obsolete, to be ignored)
+        # Remove spaces and end of lines after the 
+        # commands? If no, they can lead to empty lines
+        'quote-arg' => {'skip' => 'line'},
+        'allow-recursion' => {'skip' => 'line'},
+     );
+
+# commands that should only appear at the root level and contain up to
+# the next root command
+my %root_commands;
+
+foreach my $sectioning_command (
+              'top',
+              'chapter',
+              'unnumbered',
+              'chapheading',
+              'appendix',
+              'section',
+              'unnumberedsec',
+              'heading',
+              'appendixsec',
+              'subsection',
+              'unnumberedsubsec',
+              'subheading',
+              'appendixsubsec',
+              'subsubsection',
+              'unnumberedsubsubsec',
+              'subsubheading',
+              'appendixsubsubsec',
+              'part',
+              'appendixsection',
+              'majorheading',
+              'chapheading',
+              'centerchap'
+)
+{
+  $misc_commands{$sectioning_command} = { 'arg' => 'line' };
+  $root_commands{$sectioning_command} = 1 unless ($sectioning_command =~ 
/heading/)
+}
+
+$root_commands{'node'} = 1;
+
+
+# deep copy of a structure
+sub _deep_copy ($)
+{
+    my $struct = shift;
+    my $string = Data::Dumper->Dump([$struct], ['struct']);
+    eval $string;
+    return $struct;
+}
+
+# initialize a parser
+sub parser($;$)
+{
+  my $class = shift;
+  my $conf;
+
+  my $parser = _deep_copy(\%default_configuration);
+  # _deep_copy doesn't handle subs
+  $parser->{'gettext'} = $default_configuration{'gettext'};
+
+  # called not object-oriented
+  if (ref($class) eq 'HASH')
+  {
+    #print STDERR "Not oo\n"
+    $conf = $class;
+    bless $parser;
+  }
+  elsif (ref($class))
+  { # called on an existing parser, interpreted as a duplication
+     my $old_parser = $class;
+     $class = ref($class);
+     $parser = _deep_copy($old_parser);
+     $parser->{'gettext'} = $old_parser->{'gettext'};
+     bless $parser, $class;
+     $conf = shift;
+  }
+  else
+  {
+     bless $parser, $class;
+     $conf = shift;
+  }
+  if (defined($conf))
+  {
+     foreach my $key (keys(%$conf))
+     {
+       if (exists($default_configuration{$key}))
+       {
+           if (ref($conf->{$key}) ne 'CODE')
+           {
+              $parser->{$key} = _deep_copy($conf->{$key});
+           }
+           else
+           {
+              $parser->{$key} = $conf->{$key};
+           }
+           $parser->{'no_warn'} = 1 if ($key eq 'error' and $conf->{$key} ne 
'generate' and !exists($conf->{'no_warn'}));
+           if ($key eq 'test' and $conf->{$key})
+           {
+              $parser->{'force'} = 1;
+              $parser->{'error_limit'} = 1000;
+           }
+       }
+       else
+       {
+          warn "$key not a possible configuration in 
Texinfo::Parser::parser\n";
+       }
+     }
+  }
+  $parser->{'misc_commands'} = _deep_copy (\%misc_commands);
+  foreach my $name (@{$parser->{'indices'}}, @default_index_names)
+  {
+    $parser->{'misc_commands'}->{$name.'index'} = { 'arg' => 'line' };
+  }
+  return $parser;
+}
+
+sub parse_texi_text($$;$)
+{
+  my $self = shift;
+  my $text = shift;
+  my $lines_nr = shift;
+  if (!ref($text))
+  {
+    $text = [ map {$_."\n"} split /\n/, $text ];
+  }
+  if (defined($lines_nr) and !ref($lines_nr))
+  {
+    my $first_line = $lines_nr;
+    $lines_nr = [];
+    foreach my $index(0..scalar(@$text)-1)
+    {
+       $lines_nr->[$index] = { 'line_nr' => ($index+$first_line), 'file_name' 
=> '', 'macro' => '' };
+    }
+  }
+  return $self->_internal_parse_text($text, $lines_nr);
+}
+
+sub parse_texi_line($$;$)
+{
+  my $self = shift;
+  return $self->_internal_parse_text([$_[0]], [$_[1]], 1);
+}
+
+sub tree_to_texi ($);
+
+# internal sub
+
+sub _line_warn($$$)
+{
+    my $parser = shift;
+    return if ($parser->{'no_warn'});
+    my $text = shift;
+    chomp ($text);
+    my $line_number = shift;
+    return if (!defined($line_number));
+    my $file = $line_number->{'file_name'};
+    # otherwise out of source build fail since the file names are different
+    $file =~ s/^.*\/// if ($parser->{'test'});
+    if ($line_number->{'macro'} ne '')
+    {
+        warn sprintf($parser->__("%s:%d: warning: %s (possibly involving 
address@hidden)\n"), $file, $line_number->{'line_nr'}, $text, 
$line_number->{'macro'});
+    }
+    else
+    {
+        warn sprintf($parser->__("%s:%d: warning: %s\n"), $file, 
$line_number->{'line_nr'}, $text);
+    }
+}
+
+my $error_nrs = 0;
+sub _check_errors($)
+{
+   my $parser = shift;
+   $error_nrs ++;
+   if ($error_nrs >= $parser->{'error_limit'})
+   {
+      warn $parser->__("Too many errors!  Gave up.\n") if ($parser->{'error'} 
eq 'generate');
+      return 1;
+   }
+   return 0;
+}
+
+sub _line_error($$$)
+{
+    my $parser = shift;
+    my $text = shift;
+    chomp ($text);
+    my $line_number = shift;
+    if (defined($line_number))
+    {
+       my $file = $line_number->{'file_name'};
+       $file =~ s/^.*\/// if ($parser->{'test'});
+       my $macro_text = '';
+       $macro_text = " (possibly involving address@hidden>{'macro'})" if 
($line_number->{'macro'} ne '');
+       my $error_text = "$file:$line_number->{'line_nr'}: $text$macro_text\n";
+       if ($parser->{'error'} eq 'generate')
+       {
+          warn "$error_text";
+          return 1 unless ($parser->{'force'});
+       }
+       else
+       {
+          return $error_text unless ($parser->{'force'});
+       }
+    }
+    return (_check_errors($parser));
+}
+
+sub _parse_macro_command($$)
+{
+  my $line = shift;
+  my $parent = shift;
+  my $macro;
+  if ($line =~ /^\s+(\w[\w-]*)\s*(.*)/)
+  {
+     my $macro_arg_name = $1;
+     my $macro_arg_args = $2;
+     $macro = { 'cmdname' => 'macro', 'parent' => $parent, 'contents' => [] };
+     $macro->{'args'} = [ { 'type' => 'macro_arg_name', 'text' =>  
$macro_arg_name, 'parent' => $macro },
+                        { 'type' => 'macro_arg_args', 'text' => 
$macro_arg_args, 'parent' => $macro} ];
+  }
+  return $macro;
+}
+
+#c 'menu_entry'
+# t 'menu_entry_leading_text'
+#
+#t 'macro_arg_name'
+#t 'macro_arg_args'
+#
+#t 'raw'
+#
+#t 'misc_arg'
+#c 'misc_line_arg'
+#
+#c 'block_line_arg'
+#
+#c 'brace_command_arg'
+#
+#special for @verb, type is the character
+
+# the main subroutine
+sub _internal_parse_text($$;$$)
+{
+  my $self = shift;
+  my $text = shift;
+  my $line_nr = shift;
+  my $no_para = shift;
+
+  # FIXME find on the tree
+  my $in_menu;
+  my $in_deff_line;
+  #my @separators;
+  my $new_line = '';
+  my $maybe_menu_entry;
+
+  my $root = { 'contents' => [] };
+  my $current = $root;
+
+  while (@$text)
+  {
+     my $new_text = shift @$text;
+     # FIXME error? Or accept? Or nothing special?
+     #next if ($new_text = '');
+
+     $new_line .= $new_text;
+     my $line_nr = shift @$line_nr;
+
+     my $chomped_text = $new_text;
+     if (@$text and !chomp($chomped_text))
+     {
+        next; 
+     }
+     
+     my $line = $new_line;
+     $new_line = '';
+
+     if ($self->{'debug'})
+     {
+       print STDERR "NEW LINE; $line";
+       print STDERR "".Data::Dumper->Dump([$root], ['$root']);
+     }
+     # to determine if it is a menu entry, check ^*, and if set, add
+     # : to the separators list.
+
+     if ($in_menu)
+     {
+        if ($line =~ s/^(\*\s+)//)
+        {
+           my $leading_text = $1;
+           $maybe_menu_entry = ':';
+           #push @separators, ':';
+           push @{$current->{'contents'}}, 
+                          { 'type' => 'menu_entry',
+                              'args' => [ { 'type' => 
'menu_entry_leading_text',
+                                            'text' => $leading_text } ]
+                           };
+        }
+     }
+
+     while (1)
+     {
+        if ($current->{'cmdname'} and $block_commands{$current->{'cmdname'}} 
and ($block_commands{$current->{'cmdname'}} eq 'raw'))
+        {
+           # special case for macro that may be nested
+           my $macro;
+           if ($current->{'cmdname'} eq 'macro' and $line =~ /address@hidden/)
+           {
+               my $mline = $line;
+               $mline =~ s/address@hidden//;
+               $macro = _parse_macro_command ($mline, $current);
+           }
+           if ($macro)
+           {
+               push @{$current->{'contents'}}, $macro;
+               $current = $current->{'contents'}->[-1];
+               last;
+           }
+           elsif ($line =~ /^(.*?)address@hidden([a-zA-Z][\w-]*)/o and ($2 eq 
$current->{'cmdname'}))
+           {
+               $line =~ s/^(.*?)(address@hidden>{'cmdname'})//;
+               push @{$current->{'contents'}}, { 'text' => $1, 'type' => 'raw' 
} if ($1 ne '');
+               $current = $current->{'parent'};
+               last unless ($line =~ /\S/);
+           }
+           else
+           {
+               push @{$current->{'contents'}}, { 'text' => $line, 'type' => 
'raw' };
+               last;
+           }
+        }
+        elsif ($current->{'type'} and $current->{'parent'}->{'cmdname'} and 
$current->{'parent'}->{'cmdname'} eq 'verb')
+        { # type should be 'brace_command_arg'
+           my $char = quotemeta($current->{'type'});
+           if ($line =~ s/^(.*?)$char\}/\}/)
+           {
+               push @{$current->{'contents'}}, { 'text' => $1, 'type' => 
'raw', 'parent' => $current } if ($1 ne '');
+               
+           }
+           else
+           {
+               push @{$current->{'contents'}}, { 'text' => $line, 'type' => 
'raw', 'parent' => $current };
+               last;
+           }
+        }
+        $line =~ s/^([^{}@,]*)//;
+        push @{$current->{'contents'}}, { 'text' => $1, 'parent' => $current } 
if ($1 ne '');
+        
+        # separators: $maybe_menu_entry$command_comma$maybe_menu_name
+        if ($line =~ s/address@hidden([a-zA-Z][\w-]*)//)
+        {
+           my $end_command = $1;
+           print STDERR "END COMMAND $end_command\n" if ($self->{'debug'});
+           # close paragraph
+           # close other @-commands with braces
+           # check that the format is right
+
+           # end format
+           $current = $current->{'parent'};
+           last unless ($line =~ /\S/);
+        }
+        elsif ($line =~ s/^\@(["'address@hidden,\.!\?\s\*\-\^`=:\|\/\\])//o or 
$line =~ s/^\@([a-zA-Z][\w-]*)//o)
+        {
+           my $command = $1;
+           $command = $self->{'aliases'}->{$command} if 
(exists($self->{'aliases'}->{$command}));
+           print STDERR "COMMAND $command\n" if ($self->{'debug'});
+           if (defined($deprecated_commands{$command}))
+           {
+              if ($deprecated_commands{$command} eq '')
+              {
+                 _line_warn($self, sprintf($self->__("%c%s is obsolete."), 
ord('@'), $command), $line_nr);
+              }
+              else
+              {
+                 _line_warn($self, sprintf($self->__("%c%s is obsolete; 
%s"),ord('@'), $command, $self->__($deprecated_commands{$command})), $line_nr);
+              }
+           }
+           if (defined($self->{'misc_commands'}->{$command}))
+           {
+              my ($args, $line_arg, $error);
+              ($line, $args, $line_arg, $error) = 
$self->_parse_misc_command($line, $command, $line_nr);
+              return $error if ($error);
+              push @{$current->{'contents'}},  { 'cmdname' => $command, 
'parent' => $current };
+              
+              foreach my $arg (@$args)
+              {
+                 push @{$current->{'contents'}->[-1]->{'args'}},
+                    { 'type' => 'misc_arg', 'text' => $arg, 'parent' => 
$current->{'contents'}->[-1] };
+              }
+              if (defined($line_arg))
+              {
+                 $line = $line_arg;
+                 $current = $current->{'contents'}->[-1];
+                 $current->{'args'} = [  { 'type' => 'misc_line_arg', 
'contents' => [], 'parent' => $current } ];
+                 # @node is the only misc command with args separated with 
comma
+                 $current->{'remaining_args'} = 4 if ($command eq 'node');
+                 $current = $current->{'args'}->[-1];
+              }
+              # FIXME @tab and @item, special case for @item(x) in @table...
+           }
+           elsif (exists($block_commands{$command}))
+           {
+              my $macro;
+              if ($command eq 'macro')
+              {
+                $macro = _parse_macro_command ($line, $current);
+              }
+              if ($macro)
+              {
+                 push @{$current->{'contents'}}, $macro;
+                 $current = $current->{'contents'}->[-1];
+                 last;
+              }
+              else
+              {
+                 $line =~ s/\s*//;
+                 push @{$current->{'contents'}}, { 'cmdname' => $command, 
'parent' => $current };
+                 $current = $current->{'contents'}->[-1];
+                 if ($block_commands{$command} and $block_commands{$command} 
=~ /^\d+$/)
+                 {
+                    $current->{'args'} = [ { 'type' => 'block_line_arg', 
'contents' => [], 'parent' => $current } ];
+                    $current->{'remaining_args'} = $block_commands{$command} 
-1;
+                    $current = $current->{'args'}->[-1];
+                 }
+                 elsif ($command eq 'multitable')
+                 {
+                    if ($line =~ s/address@hidden//)
+                    { # both a cmdname and block_line_arg
+                       $current->{'args'} = [ { 'cmdname' => 
'columnfractions', 'type' => 'block_line_arg', 'parent' => $current, 'contents' 
=> [] } ];
+                       $current = $current->{'args'}->[-1];
+                    }
+                 }
+                 else
+                 {
+                    last unless ($line =~ /\S/);
+                 }
+              }
+              # FIXME multitable and deff*
+           }
+           elsif ($line =~ s/^{// and (defined($brace_commands{$command}) or 
defined($accent_commands{$command})))
+           {
+               push @{$current->{'contents'}}, { 'cmdname' => $command, 
'parent' => $current };
+               $current = $current->{'contents'}->[-1];
+               if ($command eq 'verb')
+               {
+                   if ($line =~ /^$/)
+                   {
+                        my $error = _line_error ($self, 
sprintf($self->__("address@hidden without associated character"), $command), 
$line_nr);
+                        return $error if ($error);
+                   }
+                   else
+                   {
+                       $line =~ s/^(.)//;
+                       $current->{'type'} = $1;
+                   }
+               }
+               if ($brace_commands{$command} or $accent_commands{$command})
+               {
+                 $current->{'args'} = [ { 'type' => 'brace_command_arg', 
'parent' => $current, 'contents' => [] } ];
+                 if ($brace_commands{$command})
+                 {
+                    $current->{'remaining_args'} = $brace_commands{$command} 
-1;
+                 }
+                 $current = $current->{'args'}->[-1];
+               }
+           }
+           elsif ($accent_commands{$command})
+           {
+                if ($command =~ /^[a-zA-Z]/)
+                {
+                    $line =~ s/^\s*//;
+                }
+                elsif ($line =~ /^\s/)
+                {
+                    _line_warn ($self, sprintf($self->__("Accent command 
address@hidden' must not be followed by whitespace"), $command), $line_nr);
+                }
+                if ($line =~ /^\@/)
+                {
+                    my $error = _line_error ($self, sprintf($self->__("Use 
braces to give a command as an argument to address@hidden"), $command), 
$line_nr);
+                    return $error if ($error);
+                }
+                if ($line =~ s/^(\S)//o)
+                {
+                    my $accent = { 'cmdname' => $command, 'parent' => $current 
};
+                    $accent->{'args'} = [ { 'text' => $1, 'parent' => $accent 
} ];
+                    push @{$current->{'contents'}}, $accent;
+                }
+                else
+                { # The accent is at end of line
+                    # FIXME warn? And test case? Maybe this is catched 
+                    # above, by "Accent command address@hidden' must not be 
followed by whitespace"
+                    # for commands with letter.
+                    push @{$current->{'contents'}}, { 'text' => $command, 
'parent' => $current };
+                }
+           }
+           elsif ($no_brace_commands{$command})
+           {
+              push @{$current->{'contents'}}, { 'cmdname' => $command, 
'parent' => $current };
+           }
+           else
+           {
+              # unknown
+           }
+        }
+        elsif ($line =~ s/^([{}@,])//)
+        {
+           my $separator = $1;
+           print STDERR "SEPARATOR: $separator\n" if ($self->{'debug'});
+           if ($separator eq '@')
+           {
+              my $error = _line_error ($self, $self->__("Unexpected \@"), 
$line_nr);
+              return $error if ($error);
+           }
+           elsif ($separator eq '{')
+           {
+              if ($current->{'cmdname'} and 
($block_commands{$current->{'cmdname'}} eq 'multitable' or 
$block_commands{$current->{'cmdname'}} eq 'bracketed'))
+              {
+                 push @{$current->{'args'}}, { 'type' => 'bracketed', 
'contents' => [], 'parent' => $current };
+                 $current = $current->{'args'}->[-1];
+              }
+              else
+              {
+                 my $error = _line_error ($self, sprintf($self->__("Misplaced 
%c"), ord('{')), $line_nr);
+                 return $error if ($error);
+              }
+           }
+           elsif ($separator eq '}')
+           { 
+              # FIXME use parents
+              if ($current->{'type'} and ($current->{'type'} eq 'bracketed' or 
$current->{'type'} eq 'brace_command_arg'))
+              {
+                 $current = $current->{'parent'};
+              }
+              else
+              {
+                 my $error = _line_error ($self, sprintf($self->__("Misplaced 
%c"), ord('}')), $line_nr);
+                 return $error if ($error);
+              }
+           }
+           elsif ($separator eq ',')
+           {
+              if ($current->{'parent'}->{'remaining_args'})
+              {
+                 $line =~ s/^\s*//;
+                 my $type = $current->{'type'};
+                 $current = $current->{'parent'};
+                 $current->{'remaining_args'}--;
+                 push @{$current->{'args'}}, { 'type' => $type, 'parent' => 
$current, 'contents' => [] };
+                 $current = $current->{'args'}->[-1];
+              }
+              else
+              { # FIXME merge with previous text if possible
+                push @{$current->{'contents'}}, { 'text' => ',', 'parent' => 
$current };
+              }
+           }
+        }
+        else
+        {
+           {
+             #local $Data::Dumper::Maxdepth = 5;
+             #local $Data::Dumper::Indent= 1;
+             #local $Data::Dumper::Terse = 1;
+             #print STDERR "END LINE: ".Data::Dumper->Dump([$current]) if 
($self->{'debug'})
+             if ($self->{'debug'})
+             {
+               print STDERR "END LINE: ";
+               print STDERR "type : $current->{'type'}, " if 
($current->{'type'});
+               print STDERR "cmdname : $current->{'cmdname'}, " if 
($current->{'cmdname'});
+               print STDERR "\n";
+             }
+           }
+           if ($line ne '')
+           {
+              die "Remaining line: $line\n";
+           }
+           if ($current->{'type'} and ($current->{'type'} eq 'block_line_arg' 
or $current->{'type'} eq 'misc_line_arg'))
+           {
+              if ($current->{'cmdname'} and $current->{'cmdname'} eq 
'columnfractions')
+              { # the columnfraction content should be text only, maybe 
followed by a comment
+                #print STDERR "COLUMNFRACTIONS: 
".Data::Dumper->Dump([$current], ['$columnfractions']) if ($self->{'debug'});
+                my @fractions;
+                my $other_contents;
+                if (address@hidden>{'contents'}})
+                {
+                  my $error = _line_error ($self, sprintf($self->__("Empty 
address@hidden"), $current->{'cmdname'}), $line_nr);
+                  return $error if ($error);
+                }
+                elsif (!defined($current->{'contents'}->[0]->{'text'}))
+                {
+                  my $error = _line_error ($self, 
sprintf($self->__("address@hidden accepts only fractions as argument"), 
$current->{'cmdname'}), $line_nr);
+                  return $error if ($error);
+                  $other_contents = $current->{'contents'};
+                }
+                else
+                {
+                  my $fraction_argument = shift @{$current->{'contents'}};
+                  # verify that the only remaining argument is a comment
+                  if (@{$current->{'contents'}} and 
(!$current->{'contents'}->[0]->{'cmdname'} 
+                      or ($current->{'contents'}->[0]->{'cmdname'} ne 'c' and 
$current->{'contents'}->[0]->{'cmdname'} ne 'comment')))
+                  {
+                     _line_warn ($self, sprintf($self->__("Unexpected argument 
on address@hidden line: %s"), $current->{'cmdname'}, tree_to_texi( { 
$current->{'contents'} })), $line_nr);
+                  }
+                  $other_contents = $current->{'contents'};
+                  # now parse the fractions
+                  my @possible_fractions = split /\s+/, 
$fraction_argument->{'text'};
+                  foreach my $fraction (@possible_fractions)
+                  {
+                     if ($fraction =~ /^(\d*\.\d+)|(\d+)\.?$/)
+                     {
+                       push @fractions, $fraction;
+                     }
+                     else
+                     {
+                        my $error = _line_error ($self, 
sprintf($self->__("column fraction not a number: %s"), $fraction), $line_nr);
+                        return $error if ($error);
+                     }
+                  }
+                }
+                $current = $current->{'parent'};
+                $current->{'args'} = [ { 'cmdname' => 'columnfractions', 
'parent' => $current } ];
+                foreach my $content (@$other_contents)
+                {
+                   $content->{'parent'} = $current;
+                   push @{$current->{'args'}}, $content;
+                }
+                $current = $current->{'args'}->[0];
+                foreach my $fraction (@fractions)
+                {
+                   push @{$current->{'args'}}, { 'type' => 'fraction', 'text' 
=> $fraction, 'parent' => $current };
+                }
+              }
+              $current = $current->{'parent'};
+           }
+           last;
+        }
+     }
+  }
+  return $root;
+}
+
+sub tree_to_texi ($)
+{
+  my $root = shift;
+  die "bad root type (".ref($root).") $root\n" if (ref($root) ne 'HASH');
+  my $result = '';
+  #print STDERR "$root ";
+  #print STDERR "$root->{'type'}" if (defined($root->{'type'}));
+  #print STDERR "\n";
+  if (defined($root->{'text'}))
+  {
+    $result .= $root->{'text'};
+  }
+  else
+  {
+    if ($root->{'cmdname'})
+    {
+      #print STDERR "cmd: $root->{'cmdname'}\n";
+      $result .= _expand_cmd_args_to_texi($root);
+    }
+    #print STDERR "$root->{'contents'} @{$root->{'contents'}}\n" if 
(defined($root->{'contents'}));
+    if (defined($root->{'contents'}))
+    {
+      die "bad contents type(".ref($root->{'contents'}).") 
$root->{'contents'}\n" if (ref($root->{'contents'}) ne 'ARRAY');
+      foreach my $child (@{$root->{'contents'}})
+      {
+        $result .= tree_to_texi($child);
+      }
+    }
+    if ($root->{'cmdname'} and (defined($block_commands{$root->{'cmdname'}})))
+    {
+      $result .= '@end '.$root->{'cmdname'} ."\n"; # ."\n"?
+    }
+  }
+  #print STDERR "tree_to_texi result: $result\n";
+  return $result;
+}
+
+sub _expand_cmd_args_to_texi ($)
+{
+  my $cmd = shift;
+  my $result = '@'.$cmd->{'cmdname'};
+  #print STDERR "Expand $result\n";
+  my $cmd_with_braces = 1 if (defined($brace_commands{$cmd->{'cmdname'}}) or 
defined($accent_commands{$cmd->{'cmdname'}}));
+  $result .= '{' if ($cmd_with_braces);
+  if ($cmd->{'cmdname'} eq 'verb')
+  {
+     $result .= $cmd->{'type'}.$cmd->{'args'}->[0]->{'text'}.$cmd->{'type'};
+  }
+  # must be before the next condition
+  elsif ($block_commands{$cmd->{'cmdname'}} and 
($block_commands{$cmd->{'cmdname'}} eq 'bracketed' or 
$block_commands{$cmd->{'cmdname'}} eq 'multitable'))
+  {
+     foreach my $arg (@{$cmd->{'args'}})
+     {
+        my $arg_expanded = tree_to_texi ($arg);
+        $arg_expanded = '{'.$arg_expanded.'}' if ($arg->{'type'} and 
$arg->{'type'} eq 'bracketed');
+        $result .= ' '.$arg_expanded;
+     }
+  }
+  elsif (($cmd_with_braces or ($block_commands{$cmd->{'cmdname'}} and 
$block_commands{$cmd->{'cmdname'}} ne 'raw')) 
+      and defined($cmd->{'args'}))
+  {
+    die "bad args type (".ref($cmd->{'args'}).") $cmd->{'args'}\n" if 
(ref($cmd->{'args'}) ne 'ARRAY');
+    foreach my $arg (@{$cmd->{'args'}})
+    {
+       $result .= tree_to_texi ($arg) . ', ';
+    }
+    $result =~ s/, $//;
+  }
+  elsif ($cmd->{'cmdname'} eq 'macro')
+  {
+     $result .= ' ' .$cmd->{'args'}->[0]->{'text'}. ' 
'.$cmd->{'args'}->[1]->{'text'};
+  }
+  elsif (defined($cmd->{'args'}))
+  {
+     #print STDERR "".Data::Dumper->Dump([$cmd]);
+     foreach my $arg (@{$cmd->{'args'}})
+     {
+       
+        $result .= ' ' unless ($cmd->{'cmdname'} eq 'c' or $cmd->{'cmdname'} 
eq 'comment');
+        $result .= tree_to_texi ($arg);
+     }
+     #die "Shouldn't have args: $cmd->{'cmdname'}\n";
+  }
+  if ($cmd_with_braces)
+  {
+     $result .= '}';
+  }
+  elsif (defined($block_commands{$cmd->{'cmdname'}}))
+  {
+     # there is an end of line if there is a comment, for example
+     chomp($result);
+     $result .= "\n";
+  }
+  #print STDERR "Result: $result\n";
+  return $result;
+}
+
+# return the line after preserving things according to misc_commands map.
+sub _parse_misc_command($$$$)
+{
+    my $self = shift;
+    my $line = shift;
+    my $command = shift;
+    my $line_nr = shift;
+    my $args = [];
+    my $line_arg;
+    my $skip_spec = '';
+    my $arg_spec = '';
+
+#print STDERR "HHHHHHHHH $line $command\n";
+    $skip_spec = $self->{'misc_commands'}->{$command}->{'skip'}
+        if (defined($self->{'misc_commands'}->{'skip'}));
+    $arg_spec = $misc_commands{$command}->{'arg'}
+        if (defined($misc_commands{$command}->{'arg'}));
+
+    if ($command eq 'alias')
+    {
+       if ($line =~ s/(\s+)([a-zA-Z][\w-]*)(\s*=\s*)([a-zA-Z][\w-]*)(\s*)//)
+       {
+          $self->{'aliases'}->{$2} = $4;
+          $args = [$2, $4];
+       }
+       else
+       {
+          my $error = _line_error ($self, sprintf($self->__("Bad argument to 
address@hidden"), $command), $line_nr);
+          return ('', '', '', $error);
+       }
+    }
+    elsif ($command eq 'definfoenclose')
+    {
+       if ($line =~ s/^\s+([a-z][\w\-]*)\s*,\s*([^\s]+)\s*,\s*([^\s]+)//)
+       {
+          $args = [$1, $2, $3 ];
+          $self->{'info_enclose'}->{$1} = [ $2, $3 ];
+       }
+       else
+       {
+          my $error = _line_error ($self, sprintf($self->__("Bad argument to 
address@hidden"), $command), $line_nr);
+          return ('', '', '', $error);
+       } # FIXME warn about garbage remaining on the line?
+    }
+    elsif ($command eq 'set')
+    {
+      if ($line =~ /^(\s+)([\w\-]+)(\s+)(.*)$/)
+      {
+        $args = [$2, $4];
+      }
+      else
+      {
+        my $error = _line_error ($self, sprintf($self->__("%c%s requires a 
name"), ord('@'), $command), $line_nr);
+        return ('', '', '', $error);
+      }
+      $line = '';
+    }
+    elsif ($command eq 'defindex' || $command eq 'defcodeindex')
+    {
+        if ($line =~ s/^\s+(\w+)\s*//)
+        {
+            my $name = $1;
+            if ($forbidden_index_name{$name})
+            {
+                my $error = _line_error($self, sprintf($self->__("Reserved 
index name %s"),$name), $line_nr);
+                return ('', '', '', $error);
+            }
+            else
+            {
+                $self->{'misc_commands'}->{$name.'index'} = { 'arg' => 'line' 
};
+            }
+        }
+        else
+        {
+            my $error = _line_error ($self, sprintf($self->__("Bad argument to 
address@hidden: %s"), $command, $line), $line_nr);
+            return ('', '', '', $error);
+        }
+    }
+    elsif ($arg_spec eq 'line' or $arg_spec eq 'lineraw')
+    {
+        $line =~ s/^[ \t]*// unless ($command eq 'c' or $command eq 'comment');
+        $args = [ $line ];
+        if ($arg_spec eq 'line')
+        {
+           $line_arg = $line;
+        }
+        else
+        {
+           $args = [ $line ];
+        }
+        $line = '';
+    }
+    elsif ($arg_spec)
+    {
+        my $arg_nr = $misc_commands{$command}->{'arg'};
+        while ($arg_nr)
+        {
+            if ($line =~ s/^(\s+)(\S*)//o)
+            {
+                my $argument = $2;
+                push @$args, $argument if ($argument ne '');
+            }
+            else
+            {
+                last;
+            }
+            $arg_nr--;
+        }
+    }
+
+    if ($skip_spec eq 'line')
+    {
+        $line = '';
+    }
+    elsif ($skip_spec eq 'whitespace')
+    {
+        $line =~ s/^(\s*)//o;
+    }
+    elsif ($skip_spec eq 'space')
+    {
+        $line =~ s/^([ \t]*)//o;
+    }
+    # FIXME is the following useful?
+    $line = '' if (!defined($line));
+    return ($line, $args, $line_arg, '');
+}
+
+
+1;
+__END__
+# Below is stub documentation for your module. You'd better edit it!
+
+=head1 NAME
+
+Texinfo::Parser - Perl extension for blah blah blah
+
+=head1 SYNOPSIS
+
+  use Texinfo::Parser;
+  blah blah blah
+
+=head1 DESCRIPTION
+
+Stub documentation for Texinfo::Parser, created by h2xs. It looks like the
+author of the extension was negligent enough to leave the stub
+unedited.
+
+Blah blah blah.
+
+=head2 EXPORT
+
+None by default.
+
+
+
+=head1 SEE ALSO
+
+Mention other useful documentation such as the documentation of
+related modules or operating system documentation (such as man pages
+in UNIX), or any relevant external documentation such as RFCs or
+standards.
+
+If you have a mailing list set up for your module, mention it here.
+
+If you have a web site set up for your module, mention it here.
+
+=head1 AUTHOR
+
+Patrice Dumas, E<lt>address@hidden<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2010 by Patrice Dumas
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.1 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut

Index: t/01use.t
===================================================================
RCS file: t/01use.t
diff -N t/01use.t
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ t/01use.t   20 Sep 2010 17:19:05 -0000      1.1
@@ -0,0 +1,32 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl Texinfo-Parser.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use strict;
+
+#use Test;
+use Test::More;
+BEGIN { plan tests => 2 };
+use Texinfo::Parser qw(:all);
+use Data::Dumper;
+use Data::Compare;
+ok(1, "modules loading"); # If we made it this far, we're ok.
+
+#########################
+
+# Insert your test code below, the Test::More module is use()ed here so read
+# its man page ( perldoc Test::More ) for help writing this test script.
+
+use vars qw($manual_tree $manual_tree_result);
+
+require 't/manual_tree.pl';
+
+is (tree_to_texi($manual_tree), $manual_tree_result, "tree_to_texi on a 
manually written tree");
+
+#print STDERR tree_to_texi($manual_tree);
+#print STDERR "".Data::Dumper->Dump([$manual_tree], ['$manual_tree']);
+# returns 1 if they are the same
+# Data::Compare::Compare($manual_tree, $manual_tree)."\n";

Index: t/02coverage.t
===================================================================
RCS file: t/02coverage.t
diff -N t/02coverage.t
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ t/02coverage.t      20 Sep 2010 17:19:05 -0000      1.1
@@ -0,0 +1,54 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl Texinfo-Parser.t'
+
+use strict;
+
+use Test::More;
+BEGIN { plan tests => 1 };
+use Texinfo::Parser qw(:all);
+use Data::Dumper;
+use Data::Compare;
+
+#require 't/manual_tree.pl';
+
+ok(1);
+
+#is (tree_to_texi($manual_tree), $manual_tree_result, "tree_to_texi on a 
manually written tree");
+
+my $test_misc = '@chapter chapter
address@hidden comment
address@hidden
+';
+
+# test nested macros and raw block commands.
+my $test_raw = '@macro truc   { arg,  ex}
+in macro \arg\
address@hidden othermacro 
+other macro
address@hidden macro
address@hidden
address@hidden macro
+
address@hidden 
+in html @end html
+
address@hidden in tex
address@hidden tex
+
address@hidden
+in verbatim
+in verbatim2
address@hidden verbatim
+'; 
+
+##my $parser_test_raw = Texinfo::Parser->parser({'debug' => 1});
+#my $parser_test_raw = Texinfo::Parser->parser();
+##print STDERR "".Data::Dumper->Dump([$parser_test_raw]);
+#my $tree_test_raw = $parser_test_raw->parse_texi_text($test_raw);
+#print STDERR "".Data::Dumper->Dump([$tree_test_raw], 
['$tree_test_raw_result']);
+#print STDERR tree_to_texi($tree_test_raw);
+
+#print STDERR tree_to_texi($manual_tree);
+#print STDERR "".Data::Dumper->Dump([$manual_tree], ['$manual_tree']);
+# returns 1 if they are the same
+# Data::Compare::Compare($manual_tree, $manual_tree)."\n";

Index: t/06columnfractions.t
===================================================================
RCS file: t/06columnfractions.t
diff -N t/06columnfractions.t
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ t/06columnfractions.t       20 Sep 2010 17:19:05 -0000      1.1
@@ -0,0 +1,65 @@
+#use strict;
+
+use Test::More;
+BEGIN { plan tests => 1 };
+use Texinfo::Parser qw(:all);
+use Data::Dumper;
+use Data::Compare;
+
+ok(1);
+
+my $generate;
+#$generate = 1;
+if ($generate) {
+  open (RES, ">t/columnfractions_results.pl") or die;
+  print RES 'use vars qw(%result_texts);'."\n\n";
+}
+else {
+  require 't/columnfractions_results.pl';
+}
+
+sub try_test($$$) 
+{
+  my $test_text = shift;
+  my $test_name = shift;
+  my $check = shift;
+  my $parser = Texinfo::Parser->parser({'test' => 1});
+  #my $parser = Texinfo::Parser->parser({'test' => 1, 'debug' => 1});
+  my $result =  $parser->parse_texi_text($test_text, 1);
+  #ok (Data::Compare::Compare($result, $check), $test_name);
+  if ($generate) {
+    local $Data::Dumper::Purity = 1;
+    print RES 'use vars qw($result_'.$test_name.");\n";
+    print RES "".Data::Dumper->Dump([$result], ['$result_'.$test_name]);
+    print RES "\n".'$result_texts{\''.$test_name.'\'} = 
\''.tree_to_texi($result)."';\n\n";
+    print STDERR "--> $test_name\n".tree_to_texi($result)."\n";
+  }
+  else {
+    ok (Data::Compare::Compare($result, $check), $test_name);
+  }
+  #exit;
+}
+
+foreach my $test (
+[ '@multitable @columnfractions 0.4 .6 5.
address@hidden multitable', 'good', $result_good ],
+[ '@multitable @columnfractions 0.4 .6 address@hidden comment
address@hidden multitable', 'good_comment', $result_good_comment ],
+[ '@multitable @columnfractions 0 1  @c space comment
address@hidden multitable', 'good_space_comment', $result_good_space_comment ],
+[ '@multitable @columnfractions aaa
address@hidden multitable', 'not_fraction', $result_not_fraction ],
+[ '@multitable @columnfractions 
address@hidden multitable', 'empty', $result_empty ],
+[ '@multitable @columnfractions @c
address@hidden multitable', 'empty_comment', $result_empty_comment ],
+[ '@multitable @columnfractions @b{3.4} 
address@hidden multitable', 'wrong_command', $result_wrong_command ]
+) {
+  try_test($test->[0], $test->[1], $test->[2])
+}
+
+if ($generate) {
+  print RES "\n1;\n";
+  close (RES);
+}

Index: t/manual_tree.pl
===================================================================
RCS file: t/manual_tree.pl
diff -N t/manual_tree.pl
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ t/manual_tree.pl    20 Sep 2010 17:19:05 -0000      1.1
@@ -0,0 +1,71 @@
+$manual_tree = { 'cmdname' => 'multitable',
+  'args' => [
+             { 'type' => 'bracketed',
+               'contents' => [
+                               {'text' => 'aaaa'},
+                            ]
+             },
+             { 'type' => 'bracketed',
+               'contents' => [
+                               {'text' => 'xx'},
+                               {'cmdname' => 'b',
+                                'args' => [
+                                            {
+                                              'type' => 'cmdarg',
+                                              'contents' => [
+                                                            { 'text' => 'rr' }
+                                                         ]
+                                            }
+                                          ]
+                               }
+                            ]
+             },
+             { 'type' => 'line',
+               'contents' => [
+                               {'text' => 'ccc'},
+                            ]
+             },
+          ],
+  'extra' => { 'number_of_columns' => 3 },
+  'contents' => [
+           { 'type' => 'multitable_title',
+             'contents' => [
+                             { 'type' => 'paragraph',
+                               'contents' => [
+                                                { 'text' => "title" },
+                                                { 'cmdname' => 'verb',
+                                                  'args' => [
+                                                               { 'text' => ' 
in verb } ',
+                                                                 'type' => 
'raw' }
+                                                            ],
+                                                  'type' => ':'
+                                                },
+                                                { 'cmdname' => '@'},
+                                                { 'text' => ".\n"},
+                                             ]
+                             }
+                           ]
+           },
+           { 'cmdname' => 'item',
+             'type' => 'multitable_row',
+             'extra' => {'row_number' => 1},
+             'contents' => [
+                             {  
+                                'type' => 'multitable_cell',
+                                'extra' => {'col_number' => 1,
+                                            'row_number' => 1},
+                                'contents' => [ {'text' => "\n" } ]
+                              }
+                           ]
+           }
+
+  ]
+};
+
+$manual_tree_result = '@multitable {aaaa} address@hidden ccc
address@hidden: in verb } :}@@.
address@hidden
address@hidden multitable
+';
+
+1;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]