📄 templateparser.pm
字号:
package TemplateParser;
use strict;
#-----------------------------------------------------------------------------
# This package parses a template file in the format explained below, and
# translates it into Perl code. See jeeves for where this package fits
# into the scheme of things.
# The template file recognizes the following directives ...
# (keywords are case insensitive)
# @OPENFILE <filename> [options] - closes the previous output file,
# the new file.
# Options:
# -append - open the file in append mode
# -no_overwrite - do not overwrite the file if it already exists.
# This is useful if you want to generate the file only once.
# -only_if_different - puts all the output into a temp file, does a
# diff with the given file, and overwrites it if the two
# files differ - useful in a make environment, where you
# don't want to unnecessarily touch the file if the contents
# are the same, to preserve timestamps
#
# @PERL <perl code> - Inserts the perl code in the output file untranslated
# @FOREACH <var> [perl condition code] - iterates thru the array @var, using
# the iterator variable $var_i. The iteration works
# wherever the condition is true.
#
# @END - terminates the loop
# @// - comment line, not reproduced in the intermediate perl file
# All other lines in the template are left essentially untranslated.
#-----------------------------------------------------------------------------
sub parse {
# Args : template file, intermediate perl file
my ($pkg,$template_file, $inter_file) = @_;
unless (open (T, $template_file)) {
warn "$template_file : $@";
return 1;
}
open (I, "> $inter_file") ||
die "Error opening intermediate file $inter_file : $@";
emit_opening_stmts($template_file);
my $line;
while (defined($line = <T>)) {
if ($line !~ /^\s*\@/) { # Is it a command?
emit_text($line);
next;
}
if ($line =~ /^\s*\@OPENFILE\s*(.*)\s*$/i) {
emit_open_file ($1);
} elsif ($line =~ /^\s*\@FOREACH\s*(\w*)\s*(.*)\s*/i) {
emit_loop_begin ($1,$2);
} elsif ($line =~ /^\s*\@END/i) {
emit_loop_end();
} elsif ($line =~ /^\s*\@PERL(.*)/i) {
emit_perl("$1\n");
};
}
emit_closing_stmts();
close(I);
return 0;
}
# All pieces of output code are within a "here" document terminated
# by _EOC_
#
#----------------------------------------------------------------------
# emit_opening_stmts
# ==> emit ("Convert ROOT's properties to global variable names")
#
sub emit_opening_stmts {
my $template_file = shift;
emit("# Created automatically from $template_file");
emit(<<'_EOC_');
use Ast;
use JeevesUtil;
$tmp_file = "jeeves.tmp";
sub open_file;
if (! (defined ($ROOT) && $ROOT)) {
die "ROOT not defined";
}
$file = "> -";
open (F, $file) || die $@;
$code = "";
$ROOT->visit();
_EOC_
}
#------------------------------------------------------------------------
# emit_open_file
# ==> emit ("Close the previous file, and open the new filename for output
#
sub emit_open_file {
my $file = shift;
my $no_overwrite = ($file =~ s/-no_overwrite//gi) ? 1 : 0;
my $append = ($file =~ s/-append//gi) ? 1 : 0;
my $only_if_different = ($file =~ s/-only_if_different//gi) ? 1 : 0;
$file =~ s/\s*//g;
emit (<<"_EOC_");
# Line $.
open_file(\"$file\", $no_overwrite, $only_if_different, $append);
_EOC_
}
#----------------------------------------------------------------------
# emit_loop_begin
# ==> emit ("manufacture an iterator name, and visit each element in
# that array")
# The best way to understand this code is to execute the schema compiler
# and look at the intermediate perl code.
#
sub emit_loop_begin {
my $l_name = shift; # Name of the list variable
my $condition = shift;
my $l_name_i = $l_name . "_i";
emit (<<"_EOC_");
# Line $.
foreach \$$l_name_i (\@\${$l_name}) {
\$$l_name_i->visit ();
_EOC_
if ($condition) {
emit ("next if (! ($condition));\n");
}
}
#----------------------------------------------------------------------
sub emit_loop_end {
emit(<<"_EOC_");
#Line $.
Ast->bye();
}
_EOC_
}
#----------------------------------------------------------------------
sub emit_perl {
emit($_[0]);
}
#----------------------------------------------------------------------
sub emit_text {
chomp $_[0];
# Escape quotes in the text
$_[0] =~ s/"/\\"/g;
$_[0] =~ s/'/\\'/g;
emit(<<"_EOC_");
output("$_[0]\\n");
_EOC_
}
#----------------------------------------------------------------------
sub emit_closing_stmts {
emit(<<'_EOC_');
Ast->bye();
close(F);
unlink ($tmp_file);
sub open_file {
my ($a_file, $a_nooverwrite, $a_only_if_different, $a_append) = @_;
#First deal with the file previously opened
close (F);
if ($only_if_different) {
if (JeevesUtil::compare ($orig_file, $curr_file) != 0) {
rename ($curr_file, $orig_file) ||
die "Error renaming $curr_file to $orig_file";
}
}
#Now for the new file ...
$curr_file = $orig_file = $a_file;
$only_if_different = ($a_only_if_different && (-f $curr_file)) ? 1 : 0;
$no_overwrite = ($a_nooverwrite && (-f $curr_file)) ? 1 : 0;
$mode = ($a_append) ? ">>" : ">";
if ($only_if_different) {
unlink ($tmp_file);
$curr_file = $tmp_file;
}
if (! $no_overwrite) {
open (F, "$mode $curr_file") || die "could not open $curr_file";
}
}
sub output {
print F @_ if (! $no_overwrite)
}
1;
_EOC_
}
#----------------------------------------------------------------------
sub emit {
print I $_[0];
}
1; # returns 1 if successfully compiled
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -