From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/local/bin/perl -w
# Highly experimental tcl -> perl converter, aimed at eventually
# completeing Tix merge
use Carp;
@operators = (
[qw(return shift next last)],
[qw(= += -=)],
[qw(?:)],
[qw([]),'()'],
[qw(|| |)],
[qw(&& &)],
[qw(< <= > >= == != =~)],
[qw(+ - .)],
[qw(* /)],
[qw(.)],
['->','&()','eval','bindsub','->{}','++','--','glob'],
[qw(lindex)],
[qw(!)]
);
my $ClassInit;
my $pri = 0;
my $group;
foreach $group (@operators)
{
$pri++;
my $op;
foreach $op (@$group)
{
$rightpri{$op} = $pri;
}
}
%leftpri = %rightpri;
%perlpri = %rightpri;
$leftpri{'.'} = 0;
$global = 1;
$InBind = 0;
%widget = ();
foreach (qw(entry menu menubutton frame text canvas scale scrollbar
button label radiobutton checkbutton))
{
$widget{$_} = \&tcl_widget;
}
sub tokenize
{
my $term = shift;
croak unless defined $term;
local ($_) = shift if (@_);
my @tokens = ();
if (/^(\s*(#.*)\n)/)
{
push(@tokens,$2);
substr($_,0,length($1)) = "";
$.++;
return @tokens;
}
while (length($_))
{
if (/^(\s*[$term]?)/s)
{
my $spc = $1;
$. += $spc =~ tr/\n/\n/;
substr($_,0,length($spc)) = "";
last if ($spc =~ /[$term]/s);
}
if (/^(\{|!?\[)/)
{
substr($_,0,length($1)) = "";
my $count = 1;
my $open = $1;
my $close;
my $var = "";
if ($open eq '{')
{
$close = '}';
}
else
{
$close = ']';
$var = $open;
$open = '[';
}
while (length($_))
{
if (/^"([^\\"]|\\.)*"/)
{
$var .= $&;
$_ = $';
}
else
{
my $ch = substr($_,0,1);
substr($_,0,1) = "";
$count++ if ($ch eq $open);
$count-- if ($ch eq $close);
last unless ($count);
$var .= $ch;
}
}
$var .= ']' if ($close eq ']');
push(@tokens,$var);
}
elsif (/^("([^\\"]|\\.)*"|([^\s$term]|\\\s)+)/)
{
my $token = $1;
die "$_" if ($token =~ /^"/ && $token !~ /"$/);
substr($_,0,length($token)) = "";
push(@tokens,$token);
}
}
return @tokens;
}
sub callback
{
local $_ = shift;
$_ = $1 if (/^"(.*)"$/);
my @stat = block($_);
if (@stat == 1)
{
my @items = @{$stat[0]};
my $op = shift(@items);
if ($op eq '->' || $op eq '&()')
{
return ['[]',@items] ;
}
warn "Weird callback:".Pretty($op,@items);
}
return ['bindsub',\@stat];
}
sub tcl_args
{
my $i = 0;
my @args = @_;
while ($i < @args)
{
local $_ = $args[$i];
if (/^-options$/)
{
$i++;
if ($i < @args)
{
my @items = tokenize("\0",$args[$i]);
$args[$i] = ['[]',@items];
}
}
elsif (/^-\w*command$/)
{
$i++;
$args[$i] = callback($args[$i]) if ($i < @args);
}
else
{
$args[$i] = expr($_) unless (/^"/);
}
$i++;
}
return @args;
}
sub tcl_call
{
my @items = ();
my $key = shift;
if (@_ && $_[0] =~ /\.\w+$/)
{
my ($base) = ($_[0] =~ /\$?(.*)/);
unless (exists $variable{$base})
{
return tcl_widget("\u$key",@_);
}
}
if (substr($key,0,1) =~ /[\$%[]/)
{
unless (@_)
{
return ['->',expr($key),'Call'];
}
if (@_ && $_[0] =~ /^\w[\w:-]*$/)
{
return ['->',expr($key),subname(shift),&tcl_args];
}
}
if (@_ && ($_[0] eq '%W' || $_[0] eq '$w'))
{
my $obj = shift;
return ['->',expr($obj),subname($key),&tcl_args];
}
return [subname($key),&tcl_args];
}
sub find_widget
{
if (defined(@vars) && @vars)
{
return $vars[0]->[1];
}
return '$Widget';
}
sub primary
{
croak "No string!" unless defined $_;
my $start = $_;
my $val = undef;
s/^\s+//;
if (/^\(/)
{
$_ = $';
$val = subexpr(0);
if (/^\s*\)/)
{
$_ = $';
}
else
{
die ") missing in '$_' from '$start'";
}
}
elsif (/^\[/)
{
substr($_,0,1) = "";
my $count = 1;
my $str = "";
while (length($_))
{
my $ch = substr($_,0,1);
substr($_,0,1) = "";
$count++ if ($ch eq '[');
$count-- if ($ch eq ']');
last unless $count;
$str .= $ch;
}
$val = translate(tokenize("\n;",$str));
}
elsif (/^\{/)
{
substr($_,0,1) = "";
my $count = 1;
$val = "";
while (length($_))
{
if (/^"([^\\"]|\\.)*"/)
{
$val .= $&;
$_ = $';
}
else
{
my $ch = substr($_,0,1);
substr($_,0,1) = "";
$count++ if ($ch eq '{');
$count-- if ($ch eq '}');
last unless $count;
$val .= $ch;
}
}
}
elsif (/^(\w+)\(/)
{
my $key = $1;
$_ = $';
my $str = "";
my $count = 1;
$val = "";
while (length($_))
{
my $ch = substr($_,0,1);
substr($_,0,1) = "";
$count++ if ($ch eq '(');
$count-- if ($ch eq ')');
last unless $count;
$val .= $ch;
}
my @args = tokenize("\0",$str);
return [$key,map(expr($_),@args)];
}
elsif (/^\$(\$*[\w:]+)/)
{
my $base = $1;
my $vname = $&;
my $index = "";
$_ = $';
if (/^\(/)
{
my $count = 0;
while (length($_))
{
my $ch = substr($_,0,1);
substr($_,0,1) = "";
$index .= $ch;
$count++ if ($ch eq '(');
$count-- if ($ch eq ')');
last unless $count;
}
}
if ($base =~ /^(\$\w+)(.+)$/)
{
$val = ['->{}',$1,$2];
$val = ['->{}',$val,$index] if (defined $index);
}
elsif ($global)
{
unless (exists $global{$base} || exists $global{$vname})
{
$global{$base} = '$'.$base;
}
$val = $global{$base}.$index;
}
else
{
unless (exists $variable{$base})
{
push(@vars,['my',($variable{$base} = '$'.$base)]);
}
$val = $variable{$base};
$val .= $index if (defined $index);
}
}
elsif (/^((#|0x)[\da-fA-F]+)/ || /^(\d+(\.\d+|[cm])?)/)
{
$val = $1;
$_ = $';
}
elsif ($_ eq '.' || /^\.\W/)
{
substr($_,0,1) = "";
$val = ['->',find_widget(),'MainWindow'];
}
elsif (/^(-((\w*|\*)-)+\*)/)
{
$val = $1;
$_ = $';
}
elsif (/^([-!\/+\@])/)
{
my $op = $1;
$_ = $';
return $op unless (length($_));
my $rhs = primary();
return $op.$rhs if ($op eq '-' && $rhs =~ /^\w+$/);
return ['.',$op,$rhs] if ($op eq '/' || $op eq '@');
return $rhs if ($op eq '+' && !ref($rhs) && $rhs =~ /\d+/);
$val = [$op,$rhs];
}
elsif (/^([\w:\$]+|"([^\\"]|\\.)*"|%\w|<[\w-]+>)/)
{
$val = $1;
$_ = $';
$val = $1 if ($val =~ /^"(\w+)"$/);
}
unless (defined $val)
{
die "operand expected '$start'";
}
return $val;
}
sub subexpr
{
my $pri = shift;
my $lhs = primary();
while (/^\s*(\.|<=?|>=?|==|&&?|\|\|?|!=|[-+*\/])/ && $pri <= $leftpri{$1})
{
my $op = $1;
$_ = $';
my $rhs = subexpr($rightpri{$op});
if (!ref($rhs) && !length($rhs) && ($op eq '==' || $op eq '!='))
{
$lhs = ['defined',$lhs];
$lhs = ['!',$lhs] if ($op eq '==');
}
elsif (!ref($rhs) && $rhs eq '0' && ($op eq '==' || $op eq '!='))
{
$lhs = ['!',$lhs] if ($op eq '==');
}
elsif ($op eq '.')
{
$lhs = ['->{}',$lhs,'.'.$rhs];
}
else
{
$lhs = [$op,$lhs,$rhs];
}
}
return $lhs;
}
sub expr
{
local ($_) = @_;
croak "No arg" unless (defined $_);
return "" if (/^\s*$/);
my $val;
eval { $val = subexpr(0) };
croak "$@ in $_[0]" if ($@);
croak "Trailing expression:$_ in $_[0]" if (/\S/);
return $val;
}
sub translate;
sub block;
sub tcl_option
{
my $key = $_[0];
if ($key eq 'add')
{
shift;
return class_init(['->','$w','optionAdd',@_]);
}
else
{
&tcl_call;
}
}
sub tcl_return
{
@_ = tokenize("\0",shift) if (@_ == 1);
return ['return',&tcl_args];
}
sub class_init
{
unless (defined $ClassInit)
{
if (defined $subname)
{
$subname = 'ClassInit';
unshift(@vars,['my','$mw',['shift']]);
unshift(@vars,['my','$class',['shift']]);
$ClassInit = \@subbody;
return @_;
}
else
{
$ClassInit = [@_];
unshift(@$ClassInit,['my','$mw',['shift']]);
unshift(@$ClassInit,['my','$class',['shift']]);
return ['sub','ClassInit',$ClassInit];
}
}
push(@$ClassInit,@_);
return ();
}
sub tcl_bind
{
my $obj = shift;
my @args = ();
my @body = ();
my $isclass = 0;
if (substr($obj,0,1) eq '$')
{
$obj = expr($obj);
}
else
{
$isclass = 1;
unless (defined $class)
{
$class = $obj;
$prefix = "\l$class" unless (defined $prefix);
}
push(@args,'$class');
$obj = '$mw';
}
push(@args,shift);
if (@_)
{
local $global = 1;
local @vars = ();
map(s/^"(.*)"$/$1/,@_);
my @stat = block(join(' ',@_));
@stat = (['->','%W','NoOp']) unless (@stat);
if (@stat == 1 && $stat[0]->[1] eq '%W')
{
my ($op,$junk,$meth,@items) = @{shift(@stat)};
foreach (@items)
{
$_ = ['Ev',$1] if (!ref($_) && /^%(\w)$/);
}
if (@items)
{
push(@body,['[]',$meth,@items]);
}
else
{
push(@body,$meth);
}
}
else
{
if (@stat)
{
unshift(@stat,['my','$Ev',['->','$w','XEvent']]);
unshift(@stat,['my','$w',['shift']]);
}
push(@body,['bindsub',\@stat]);
}
}
my $stat = ['->',$obj,'bind',@args,@body];
return class_init($stat) if ($isclass);
return $stat;
}
sub tcl_proc
{
local $global = 0;
my @items = ();
local (%variable);
local $subname = shift(@_);
local ($_) = shift(@_);
my @args = tokenize("\0");
local @subbody = ();
local @vars = ();
if (@args)
{
my $arg;
foreach $arg (@args)
{
my @v = tokenize("\0",$arg);
my $v = shift(@v);
$variable{$v} = '$'.$v;
my $i = (!@v) ? ['shift'] : ['?:','@_',['shift'],expr(shift(@v))];
push(@vars,['my',$variable{$v},$i]);
}
}
push(@subbody,block(shift(@_)));
unshift(@subbody,@vars);
push(@items,'sub' => $subname);
push(@items,\@subbody);
return \@items;
}
sub variable
{
return expr('$'.shift);
}
sub tcl_after
{
my @args = ('->',find_widget(),'after',expr(shift));
push(@args,callback(join(' ',@_))) if (@_);
return \@args;
}
sub tix_callmethod
{
my @args = ('->',find_widget(),'after',expr(shift));
push(@args,callback(join(' ',@_))) if (@_);
return \@args;
}
sub tix_idle
{
my @args = ('->',find_widget(),'afterIdle');
push(@args,callback(join(' ',@_)));
return \@args;
}
sub tcl_incr
{
my @items = ();
my $var = variable(shift);
if (@_)
{
return ['+=',$var,expr(shift)];
}
else
{
return ['++',$var];
}
}
sub tcl_set
{
my $vname = shift(@_);
if (@_)
{
return ['=',variable($vname),expr(shift)];
}
else
{
return variable($vname);
}
}
sub tcl_widget
{
my $kind = shift;
my $path = shift;
my ($parent,$name) = ($path =~ /^(.*)\.([^.]+)$/);
if (defined($parent) && defined($name))
{
unshift(@_,Name => $2);
return ['=',expr($path),['->',expr($parent),$kind,&tcl_args]];
}
else
{
return ['=',expr($path),['->',find_widget(),$kind,&tcl_args]];
}
}
sub tcl_case
{
my @items = ();
while($_[0] =~ /^-/) { shift } # XXX -glob and -regexp not used
my $what = expr(shift(@_));
my $case = shift(@_);
$case = shift(@_) if ($case eq 'in');
push(@items,'if');
my @case = tokenize("\0",$case);
while (@case >= 2)
{
my $lab = shift(@case);
my $stat= shift(@case);
my (@lab) = tokenize("\0",$lab);
my $exp = ['==',$what,expr(shift(@lab))];
while (@lab)
{
$exp = ['||',$exp,['==',$what,expr(shift(@lab))]];
}
$stat = [block($stat)];
push(@items,$exp,$stat);
}
die Pretty(@case) if (@case);
return \@items;
}
sub tcl_catch
{
my @items = ();
push(@items,'eval');
push(@items,[block(shift(@_))]);
return \@items;
}
sub tcl_string
{
return [@_];
}
sub tcl_glob
{
while (@_ && $_[0] =~ /^-\w+/)
{
shift;
}
return ['glob',@_];
}
sub tcl_global
{
my $vname;
foreach $vname (@_)
{
$variable{$vname} = '$Tix::'.$vname;
}
return ();
}
sub tcl_uplevel
{
if ($_[0] eq '#0')
{
local $global = 1;
shift;
&translate;
}
else
{
&tcl_call;
}
}
sub tcl_upvar
{
my ($where,$what,$alias) = @_;
if ($where eq '#0' && $what =~ /^\$/)
{
$variable{$alias} = $what.'->';
return ();
}
else
{
&tcl_call;
}
}
sub tcl_unset
{
my @items = ();
my $var = variable(shift);
return ['undef',$var];
return \@items;
}
sub tcl_foreach
{
my @items = ();
push(@items,'foreach');
push(@items,variable(shift));
push(@items,expr(shift));
push(@items,[block(shift)]);
return \@items;
}
sub tix_foreach
{
my @items = ();
push(@items,'tixforeach');
my @vars = tokenize("\0",shift);
foreach (@vars)
{
$_ = variable($_);
}
push(@items,['[]',@vars]);
push(@items,expr(shift));
push(@items,[block(shift)]);
return \@items;
}
sub tcl_for
{
my @items = ();
push(@items,'for');
push(@items,expr('['. shift(@_) .']'));
push(@items,expr(shift));
push(@items,expr('['. shift(@_) .']'));
push(@items,[block(shift)]);
return \@items;
}
sub tcl_while
{
my @items = ();
push(@items,'while');
push(@items,expr(shift(@_)));
push(@items,[block(shift(@_))]);
return \@items;
}
sub tcl_regexp
{
my $regexp = shift;
my $val = shift;
return ['=~',$val,"/$regexp/"];
}
sub tcl_if
{
my @items = ();
my $key = 'if';
push(@items,'if');
while ($key eq 'if' || $key eq 'elseif')
{
push(@items,expr(shift(@_)));
push(@items,[block(shift(@_))]);
last unless (@_);
$key = shift(@_);
}
if ($key eq 'else')
{
push(@items,[block(shift(@_))]);
}
return \@items;
}
sub tcl_eval
{
my @items = ();
push(@items,'->');
push(@items,expr(shift));
push(@items,'Call',&tcl_args);
return \@items;
}
sub tcl_info
{
if (@_ == 2 && $_[0] eq 'exists')
{
return ['exists',variable($_[1])];
}
return tcl_call('info',@_);;
}
sub tcl_wm
{
my @items = ();
push(@items,'->');
my $method = shift;
croak "No arg" unless (defined $method);
my $widget = expr(shift);
push(@items,$widget,$method,&tcl_args);
return \@items;
}
sub tcl_pack
{
my @items = ();
push(@items,'->');
my $key = 'pack';
if ($_[0] =~ /^(forget|info|propagate)$/)
{
my $sub = shift;
$key .= "\u$sub";
}
my $widget = expr(shift);
push(@items,$widget,$key,&tcl_args);
return \@items;
}
sub tcl_expr
{
return expr(join(' ',@_));
}
sub tcl_format
{
my $fmt = shift;
if ($fmt =~ /^%s\(([-\w:]+)\)$/ && @_ == 1)
{
return ['->{}',expr(shift),$1];
}
return ['sprintf',$fmt,&tcl_args];
}
sub tix_class
{
$prefix = shift;
%info = tokenize("\0",shift);
if (exists $info{-classname})
{
$class = $info{-classname};
$class =~ s/^[Tt]ix//;
}
return ();
}
%tcl = ( 'proc' => \&tcl_proc,
'bind' => \&tcl_bind,
'case' => \&tcl_case,
'switch' => \&tcl_case,
'eval' => \&tcl_eval,
'info' => \&tcl_info,
'expr' => \&tcl_expr,
'format' => \&tcl_format,
'incr' => \&tcl_incr,
'after' => \&tcl_after,
'tixCallMethod' => \&tcl_call,
'tixDoWhenIdle' => \&tix_idle,
'tixWidgetDoWhenIdle' => \&tix_idle, # What is different?
'set' => \&tcl_set,
'if' => \&tcl_if,
'regexp' => \&tcl_regexp,
'while' => \&tcl_while,
'pack' => \&tcl_pack,
'wm' => \&tcl_wm,
'winfo' => \&tcl_wm,
'catch' => \&tcl_catch,
'foreach' => \&tcl_foreach,
'tixForEach' => \&tix_foreach,
'for' => \&tcl_for,
'global' => \&tcl_global,
'string' => \&tcl_string,
'glob' => \&tcl_glob,
'return' => \&tcl_return,
'upvar' => \&tcl_upvar,
'uplevel' => \&tcl_uplevel,
'unset' => \&tcl_unset,
'break' => sub { ['last'] },
'continue' => sub { ['next'] },
'tixWidgetClass' => \&tix_class,
'tixClass' => \&tix_class,
'option' => \&tcl_option,
);
sub translate
{
# &Tk::Pretty::PrintArgs;
if (@_)
{
my $key = shift;
if (substr($key,0,1) eq '#')
{
return $key;
}
elsif (exists $tcl{$key})
{
return &{$tcl{$key}};
}
elsif (exists $widget{$key})
{
return &tcl_widget("\u$key",@_);
}
else
{
eval { $what = tcl_call($key,@_) };
croak "$@ in ".join(' ',$key,@_) if ($@);
return $what;
}
}
return ();
}
sub block
{
local ($_) = @_;
croak unless defined $_;
my @stat = ();
while (length($_))
{
push(@stat,translate(tokenize("\n;")));
}
return @stat;
}
sub indent
{
my $depth = shift;
return '' if ($depth <= 0);
return ' ' x $depth;
}
sub statement;
sub expression;
sub output_block
{
my $depth = shift;
my $body = shift;
print indent($depth),"{\n";
statements($depth+1,@$body);
print indent($depth),"}";
print shift if (@_);
print "\n";
}
sub subname
{
local ($_) = shift;
croak $_ if (/^&/);
carp "Weird name ".Pretty($_) if (ref $_);
s/^${class}:+// if (defined $class);
s/^${prefix}:+// if (defined $prefix);
s/^config-//;
s/[:-]/_/g;
print STDERR "Bad '$_'\n" if (/[^\w:]/);
s/[^\w:]/_/g;
return $_;
}
sub output_sub
{
my ($depth,$key,$name,$body) = @_;
print indent($depth),"\nsub ",subname($name),"\n";
output_block($depth,$body);
}
sub output_foreach
{
my ($depth,$key,$var,$list,$body) = @_;
print indent($depth),$key," ";
expression(0,$var);
print " (";
expression(0,$list);
print ")\n";
output_block($depth+1,$body);
}
sub output_for
{
my ($depth,$key,$start,$cond,$end,$body) = @_;
print indent($depth),$key," (";
expression(0,$start);
print "; ";
expression(0,$cond);
print "; ";
expression(0,$end);
print ")\n";
output_block($depth+1,$body);
}
sub output_if
{
my $depth = shift;
my $name = shift;
if (@_ <= 3)
{
my $cond = $_[0];
croak Pretty($name,@_) unless defined $cond;
if (ref($cond) && @$cond == 2 && $cond->[0] eq '!')
{
$name = 'unless' if ($name eq 'if');
$name = 'until' if ($name eq 'while');
$_[0] = $cond = $cond->[1];
}
if (@_ == 2 && @{$_[1]} == 1 && ref($_[1]->[0]))
{
my $kind = $_[1]->[0]->[0];
unless (exists $statement{$kind})
{
print indent($depth);
expression(0,$_[1]->[0]);
print " $name (";
expression(0,$cond);
print ");\n";
return;
}
}
}
while (@_ >= 2)
{
print indent($depth),$name," (";
expression(0,shift);
print ")\n";
output_block($depth+1,shift);
$name = 'elsif';
}
if (@_)
{
print indent($depth),"else\n";
output_block($depth+1,shift);
}
}
sub output_cond
{
my ($pri,$name,$cond,$true,$false) = @_;
print '(';
expression(0,$cond);
print ') ? ';
expression(0,$true);
print " : ";
expression(0,$false);
}
sub output_diadic
{
my $pri = shift;
my $name = shift;
if (@_ == 2)
{
expression($pri,shift);
print " $name ";
expression($pri,shift);
}
else
{
print $name;
expression($pri,shift);
}
}
sub isString
{
my $op = shift;
return !ref($op) && ($op !~ /^[$%]/);
}
%strCmp = ( '<' => 'lt', '>' => 'gt',
'<=' => 'le', '>=' => 'ge',
'==' => 'eq', '!=' => 'ne');
sub output_compare
{
my ($pri,$name,$lhs,$rhs) = @_;
$name = $strCmp{$name} if (isString($lhs) || isString($rhs));
&Tk::Pretty::PrintArgs unless (defined $name);
expression($pri,$lhs);
print " $name ";
expression($pri,$rhs);
}
sub output_member
{
my ($pri,$name,$lhs,$rhs) = @_;
expression($pri,$lhs);
print "->{";
expression($pri,$rhs);
print "}";
}
sub output_prefix
{
my ($pri,$name,$right) = @_;
print "$name ";
expression($pri,$right);
}
sub output_my
{
my ($depth,$name,$left,$right) = @_;
print indent($depth),"my ";
expression(0,$left);
if (defined $right)
{
print " = ";
expression(0,$right);
}
print ";\n";
}
sub output_eval
{
my ($pri,$key,$block) = @_;
if (@$block == 1 && ref($block->[0]) && $block->[0]->[0] eq 'undef')
{
expression($pri,$block->[0]);
}
else
{
print "$key\n";
output_block($depth+1,$block);
print indent($depth);
}
}
sub output_list
{
print "(";
while (@_)
{
expression(0,shift);
print "," if (@_);
}
print ")";
}
sub output_call
{
my $pri = shift;
print subname(shift);
&output_list;
}
sub output_glob
{
my $pri = shift;
my $key = shift;
print "(";
while (@_)
{
print "<",shift,">";
print "," if (@_);
}
print ")";
}
sub output_return
{
my $pri = shift;
my $key = shift;
print "$key";
if (@_)
{
print " ";
if (@_ > 1)
{
&output_list;
}
else
{
expression(0,shift);
}
}
}
sub output_method
{
my $pri = shift;
my $op = shift;
my $obj = shift;
expression($pri,$obj);
print $op;
if (@_ > 1)
{
output_call($pri,@_) if (@_);
}
else
{
print subname(shift);
}
}
sub output_bind
{
my ($pri,$key,$body) = @_;
local $InBind = 1;
print "\n";
print indent($depth+1),"sub\n";
output_block($depth+2,$body);
print indent($depth);
}
sub output_lindex
{
my ($pri,$key,$lhs,$rhs) = @_;
expression($pri,$lhs);
print '[';
expression($pri,$rhs);
print ']';
}
sub output_group
{
my $pri = shift;
my $key = shift;
print substr($key,0,1);
while (@_)
{
expression($pri,shift);
print ',' if (@_);
}
print substr($key,1,1);
}
%expression = (
'==' => \&output_compare,
'!=' => \&output_compare,
'<=' => \&output_compare,
'<' => \&output_compare,
'>=' => \&output_compare,
'>' => \&output_compare,
'=' => \&output_diadic,
'.' => \&output_diadic,
'+=' => \&output_diadic,
'+' => \&output_diadic,
'||' => \&output_diadic,
'&' => \&output_diadic,
'&&' => \&output_diadic,
'=~' => \&output_diadic,
'[]' => \&output_group,
'()' => \&output_group,
'-' => \&output_diadic,
'*' => \&output_diadic,
'/' => \&output_diadic,
'->' => \&output_method,
'?:' => \&output_cond,
'!' => \&output_prefix,
'++' => \&output_prefix,
'bindsub' => \&output_bind,
'eval' => \&output_eval,
'lindex' => \&output_lindex,
'return' => \&output_return,
'last' => \&output_return,
'next' => \&output_return,
'shift' => \&output_return,
'glob' => \&output_glob,
'->{}' => \&output_member,
);
sub expression
{
my ($pri,$item) = @_;
croak "No item" unless defined($item);
if (ref($item))
{
if (ref($item) eq 'ARRAY')
{
my $kind = $item->[0];
if (exists $expression{$kind})
{
unless (exists $perlpri{$kind})
{
warn "Don't know priority of $kind";
$perlpri{$kind} = $perlpri{'&()'};
}
my $opri = $perlpri{$kind};
print "(" if ($opri < $pri);
&{$expression{$kind}}($opri,@$item);
print ")" if ($opri < $pri);
}
else
{
output_call($pri,@$item);
}
}
else
{
die "Not an array reference $item";
}
}
else
{
if ($item =~ /^(\$\w[^(]*)\((.*)\)$/)
{
expression($pri,"$1");
my $index;
foreach $index (split(/,/,$2))
{
print "{";
expression(0,$index);
print "}";
}
}
elsif ($item =~ /^["\$]/ || $item =~ /^-?\d+(\.\d+)?$/)
{
print $item;
}
elsif ($item =~ /^%(\w)$/)
{
if ($1 eq 'W')
{
print '$w';
}
else
{
print "\$Ev->$1";
}
}
else
{
warn "$item" if ($item =~ /\(/);
if ($item =~ /\$/)
{
print "\"$item\"";
}
else
{
print "'$item'";
}
}
}
}
%statement = ( 'sub' => \&output_sub,
'my' => \&output_my,
'if' => \&output_if,
'while' => \&output_if,
'foreach' => \&output_foreach,
'tixforeach' => \&output_foreach,
'for' => \&output_for,
);
sub statement
{
local $depth = shift;
my $item = shift;
croak "No item!" unless defined $item;
if (ref($item))
{
if (ref($item) eq 'ARRAY')
{
if (@$item)
{
my $kind = $item->[0];
if (exists $statement{$kind})
{
&{$statement{$kind}}($depth,@$item);
}
else
{
print indent($depth);
expression(0,$item);
print ";\n";
}
}
else
{
print "\n";
}
}
else
{
die "Not an array reference $item";
}
}
else
{
print indent($depth),$item,"\n";
}
}
sub statements
{
my $depth = shift;
while (@_)
{
statement($depth,shift);
}
}
$SIG{INT} = sub { croak "Interrupt" };
undef $/;
foreach $file (@ARGV)
{
if ($file =~ /\.tcl$/)
{
my $perl = $file;
$perl =~ s/\.tcl/.pm/;
open(TCL,"<$file") || die "Cannot open $file:$!";
print STDERR "$file => $perl\n";
my $prog = <TCL>;
close(TCL);
$prog =~ s/\\\n/ /sg;
my @body = block($prog);
push(@$ClassInit,['return','$class']) if (defined $ClassInit);
open(PERL,">$perl") || die "Cannot open $perl:$!";
my $old = select(PERL);
if (defined $class)
{
print "package Tk::",$class,";\n";
if (exists $info{-superclass})
{
my $superclass = $info{-superclass};
$superclass =~ s/^[Tt]ix//;
print '@Tk::',$class,'::ISA = qw(Tk::',$superclass,");\n";
}
}
statements(0,@body);
select($old);
close(PERL);
if (system("perl","-wc",$perl) != 0)
{
rename($perl,"$perl.oops");
exit(4)
}
}
}