mirror of https://github.com/GNOME/gimp.git
338 lines
7.4 KiB
Perl
Executable File
338 lines
7.4 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use 5.005;
|
|
|
|
$VERSION = 1.0;
|
|
|
|
# Copyright Marc Lehmann <pcg@goof.com>
|
|
#
|
|
# This is distributed under the GPL (see COPYING.GNU for details).
|
|
|
|
=cut
|
|
|
|
=head1 NAME
|
|
|
|
scm2scm - convert script-fu to script-fu
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
scm2scm [-d] [-t translation]... filename.scm...
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This perl-script can be used to upgrade existing script-fu-scripts to
|
|
newer gimp API's.
|
|
|
|
=head1 EXAMPLES
|
|
|
|
Convert all script-fu scripts in the current directory from the 1.0 to the
|
|
1.2 API (creating new files with the extension .sc2):
|
|
|
|
scm2scm -t 1.2 *.scm
|
|
|
|
Generate a diff containing the required changes from the 1.0
|
|
to the 1.1-API:
|
|
|
|
scm2scm -d -t 1.1 test.scm
|
|
|
|
=head1 SWITCHES
|
|
|
|
=over 4
|
|
|
|
=item -d
|
|
|
|
generate a unified diff on stdout
|
|
|
|
=item -t translation id
|
|
|
|
specify a translation id, can be one of (run scm2scm without arguments
|
|
to see the full list)
|
|
|
|
I<api1> api-mega-break-patch #1
|
|
I<api2> api-mega-rename-patch #1 (NYI)
|
|
|
|
I<1.1> 1.0 -> 1.1 (not fully implemented)
|
|
|
|
I<1.2> 1.0 -> 1.2 (not fully implemented)
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Marc Lehmann <pcg@goof.com>
|
|
|
|
=head1 SEE ALSO
|
|
|
|
gimp(1), L<Gimp>.
|
|
|
|
=cut
|
|
|
|
# Fixes names of functions by swapping last two parts of the name
|
|
# eg. gimp-image-disable-undo becomes gimp-image-undo-disable
|
|
# Whitespace is preserved(!)
|
|
sub swap_last_two {
|
|
my($a,$f,$t1,$t2,@t)=@_;
|
|
$f->[1] =~ s/(\w+)-(\w+)-(\w+)-(\w+)/$1-$2-$4-$3/;
|
|
($a,$f,new token($t1->[0],$t1->[1],$t2->[1]),@t);
|
|
}
|
|
|
|
# drop the first argument, while preserving correct whitespace(!)
|
|
sub drop_1st {
|
|
my($a,$f,$t1,$t2,@t)=@_;
|
|
($a,$f,new token($t1->[0],$t2->[1],$t2->[2]),@t);
|
|
}
|
|
|
|
# "nicify" plug-in constants
|
|
sub plug_in_constant {
|
|
my($a,$f,$t1,$t2,@t)=@_;
|
|
my $n = $t2->[1];
|
|
$n==0 and $n = "RUN_NONINTERACTIVE";
|
|
($a,$f,new token($t1->[0],$n,$t2->[2]),@t);
|
|
}
|
|
|
|
# every hash value consists of an array of specifications, each
|
|
# one has the form ["regexp", codref_to_call], or a string (another translation
|
|
# name)
|
|
%translation = (
|
|
'api1' =>
|
|
[
|
|
[
|
|
"^(gimp-airbrush|gimp-blend|gimp-brightness-contrast|gimp-bucket-fill|".
|
|
"gimp-by-color-select|gimp-channel-ops-offset|gimp-clone|gimp-color-balance|".
|
|
"gimp-color-picker|gimp-convolve|gimp-curves-explicit|gimp-curves-spline|".
|
|
"gimp-desaturate|gimp-edit-clear|gimp-edit-copy|gimp-edit-cut|gimp-edit-fill|".
|
|
"gimp-edit-paste|gimp-edit-stroke|gimp-equalize|gimp-eraser|".
|
|
"gimp-eraser-extended|gimp-flip|gimp-fuzzy-select|gimp-histogram|".
|
|
"gimp-hue-saturation|gimp-invert|gimp-levels|gimp-paintbrush|".
|
|
"gimp-paintbrush-extended|gimp-pencil|gimp-perspective|gimp-posterize|".
|
|
"gimp-rotate|gimp-scale|gimp-selection-float|gimp-selection-layer-alpha|".
|
|
"gimp-selection-load|gimp-shear|gimp-threshold)\$",
|
|
\&drop_1st
|
|
]
|
|
],
|
|
'api2' =>
|
|
[
|
|
[
|
|
"^(gimp-image-disable-undo|gimp-image-enable-undo)\$",
|
|
\&swap_last_two
|
|
]
|
|
],
|
|
'1.1' => ['nice','api1','api2'],
|
|
'1.2' => ['nice','api1','api2'],
|
|
'nice'=> [],#["^(plug-in-|file-|gimp-file-)", \&plug_in_constant]],
|
|
);
|
|
|
|
$gen_diff=0;
|
|
@trans = ();
|
|
|
|
package token;
|
|
|
|
sub new {
|
|
my $type = shift;
|
|
bless [@_],$type;
|
|
}
|
|
|
|
package main;
|
|
|
|
my $stream; # the stream to tokenize from
|
|
my $word; # the current token-word
|
|
my $tok; # current token
|
|
|
|
# parses a new token [ws, tok, ws]
|
|
sub get() {
|
|
my($ws1,$ctk,$ws2);
|
|
# could be wrapped into one regex
|
|
$ws1 = $stream=~s/^((?:\s*(?:(;[^\n]*\n))?)*)// ? $1 : die;
|
|
$ctk = $stream=~s/^(\(
|
|
|\)
|
|
|"(?:[^"]+|\\")*"
|
|
|'(?:[^()]+)
|
|
|[^ \t\r\n()]+
|
|
)
|
|
(?:[ \t]*(?=\n))?//x ? $1 : undef;
|
|
$ws2 = $stream=~s/^([ \t]*;[^\n]*\n)// ? $1 : "";
|
|
$word=$ctk;
|
|
|
|
# print "TOKEN:$ws1:$ctk:$ws2\n";
|
|
$tok=new token($ws1,$ctk,$ws2);
|
|
}
|
|
|
|
# returns a parse tree, which is an array
|
|
# of [token, token...] refs.
|
|
sub parse() {
|
|
my @toks;
|
|
$depth++;
|
|
for(;;) {
|
|
# print "$depth: $word\n";
|
|
if ($word eq "(") {
|
|
my $t = $tok; get;
|
|
my @t = &parse;
|
|
$word eq ")" or die "missing right parenthesis (got $word)\n";
|
|
push(@toks,[$t,@t,$tok]); get;
|
|
} elsif ($word eq ")") {
|
|
$depth--;
|
|
return @toks;
|
|
} elsif (!defined $word) {
|
|
$depth--;
|
|
return @toks;
|
|
} else {
|
|
push(@toks,$tok);
|
|
get;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub parse_scheme {
|
|
get;
|
|
my @t = parse;
|
|
(@t,$tok);
|
|
}
|
|
|
|
# dumb dump of the tree structure
|
|
sub dump_tree {
|
|
my $d=shift;
|
|
print "$d",scalar@_;
|
|
for(@_) {
|
|
if (isa($_,token)) {
|
|
print " [$_->[1]]";
|
|
} else {
|
|
print " *";
|
|
}
|
|
}
|
|
print "\n";
|
|
for(@_) {
|
|
if(!isa($_,token)) {
|
|
dump_tree ("$d ",@$_);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub toks2scheme {
|
|
my $func = shift;
|
|
if ($func->[1] eq "(") {
|
|
my $close = shift;
|
|
# func2scheme @_;
|
|
} else {
|
|
}
|
|
while(@_) {
|
|
my @toks = shift;
|
|
my ($unused,$t,$ws1)=$toks[0]
|
|
}
|
|
|
|
}
|
|
|
|
sub tree2scheme {
|
|
join ("",map isa($_,token) ? @$_ : tree2scheme(@$_),@_);
|
|
}
|
|
|
|
sub scheme2perl {
|
|
for(@_) {
|
|
local $_ = shift;
|
|
print scalar@_,">\n";
|
|
local *_ = \$_[0];
|
|
print "$_=\n";
|
|
if (isa($_,token)) {
|
|
my $t = $_->[1];
|
|
$_->[0] =~ s/^(\s*);/$1#/mg;
|
|
$_->[1] =~ s/^(\s*);/$1#/mg;
|
|
if ($t eq "define") {
|
|
$_->[1] = "sub";
|
|
splice @{$_[$i+1]},2,-1,new token "","{","";
|
|
$_[$i+2]
|
|
} elsif ($t =~ /[()]/) {
|
|
$_->[1] = "";
|
|
} else {
|
|
$_[0] = [
|
|
new token ("[",$_->[0],"<"),
|
|
new token ("",$_->[1],">"),
|
|
new token ("",$_->[2],"]"),
|
|
];
|
|
}
|
|
} else {
|
|
scheme2perl(@$_);
|
|
}
|
|
shift; print scalar@_,"<\n";
|
|
}
|
|
}
|
|
|
|
# translate functions, sorry folks, this function is write-only!
|
|
sub translate {
|
|
my $v=shift;
|
|
my @t=@_;
|
|
if (isa($t[0],token)) {
|
|
for(@$v) {
|
|
if ($t[1][1] =~ $_->[0]) {
|
|
@t=$_->[1]->(@t);
|
|
}
|
|
}
|
|
}
|
|
for(@t) {
|
|
$_=[translate($v,@$_)] unless isa($_,token);
|
|
}
|
|
@t;
|
|
}
|
|
|
|
sub dofile {
|
|
my($in,$out)=@_;
|
|
|
|
open IN,"$in" or die "unable to open '$in' for reading: $!";
|
|
{ local $/; $stream = <IN> }
|
|
close IN;
|
|
|
|
my @prog = parse_scheme;
|
|
|
|
if (@trans) {
|
|
my $changed;
|
|
do {
|
|
$changed=0;
|
|
@trans = map {
|
|
if (!ref $_) {
|
|
$changed=1;
|
|
@{$translation{$_}};
|
|
} else {
|
|
$_;
|
|
}
|
|
} @trans;
|
|
} while($changed);
|
|
@prog = translate ([@trans],@prog);
|
|
}
|
|
|
|
open OUT,"$out" or die "unable to open '$out' for writing: $!";
|
|
#scheme2perl(@prog);
|
|
print OUT tree2scheme(@prog);
|
|
close OUT;
|
|
}
|
|
|
|
*isa = \&UNIVERSAL::isa;
|
|
|
|
sub usage {
|
|
print STDERR "Script-Fu to Script-Fu Translater 1.1.1\n";
|
|
print STDERR "Usage: $0 [-d] [-t translation] file.scm ...\n";
|
|
print STDERR "available translations are: @{[keys %translation]}\n";
|
|
exit(1);
|
|
}
|
|
|
|
while($ARGV[0]=~/^-(.)$/) {
|
|
shift;
|
|
if ($1 eq "d") {
|
|
$gen_diff=1;
|
|
} elsif ($1 eq "t") {
|
|
push(@trans,shift);
|
|
} else {
|
|
print STDERR "unknown switch '$1'\n";
|
|
}
|
|
}
|
|
@ARGV or usage;
|
|
|
|
for $x (@ARGV) {
|
|
my $y;
|
|
if ($gen_diff) {
|
|
$y="| echo Index: '$x' && diff -u '$x' -";
|
|
} else {
|
|
($y=$x)=~s/\.scm/.sc2/i or die "source file '$x' has no .scm extension";
|
|
$y=">$y\0";
|
|
}
|
|
dofile("<$x\0",$y);
|
|
}
|
|
|