#!/usr/bin/perl -w # compile_template.pl - Don Yang (uguu.org) # # Convert HTML template to C++ source. # # 06/13/11 use strict; use constant MAX_STRING_LITERAL => 500; # Generate snippet to print a piece of string sub OutputString($) { my ($text) = @_; return if $text eq ""; # Escape special characters in string $text =~ s/\\/\\\\/g; $text =~ s/"/\\"/g; $text =~ s/\n/\\n/gs; # Output string in blocks to avoid hitting max string literal limit my $code = ""; while( length($text) > MAX_STRING_LITERAL ) { my $head = substr($text, 0, MAX_STRING_LITERAL); $text = substr($text, MAX_STRING_LITERAL); if( $head =~ /\\$/ ) { $head .= substr($text, 0, 1); $text = substr($text, 1); } $code .= "fputs(\"$head\", output);\n"; } if( $text ne "" ) { $code .= "fputs(\"$text\", output);\n"; } return $code; } # Load input template sub LoadInput($$$) { my ($input, $output_text, $output_header) = @_; open my $file, "< $input" or die $!; my $template = ""; while( my $line = <$file> ) { chomp $line; # Remove comments and compress spaces $line =~ s,//.*$,,; $line =~ s/^\s*//g; $line =~ s/\s$//g; $line =~ s/\s\s+/ /g; $line =~ s/\s+(<|>|[-+=*%\/!<>]=|\?|:|\|\|)\s+/$1/g; $line =~ s/([-+=*%\/\(\{,;])\s+/$1/g; $line =~ s/\s+([-+=*%\/\}\)])/$1/g; next if $line =~ /^$/; # Intentional spaces are specified as " ". Convert them # back to regular space. $line =~ s/ / /g; # Add to template $template .= $line . "\n"; } close $file; # Compress some more spaces $template =~ s/\s([{}])/$1/gs; $template =~ s/([{};])\s/$1/gs; $template =~ s/'\s*\+\s*'//gs; # Extract parameters from template my $prototype = "void WriteHtmlOutput(\n"; $$output_text = ""; while( $template =~ m{^(.*?) # Head /\* ([^\*]+)\s # Type ([_a-z]+)\{\*/ # Variable [^\{\}]*? # Test value /\*\}\*/ (.*)$}sx ) # Tail { my ($head, $type, $variable, $tail) = ($1, $2, $3, $4); # Print text before parameter $$output_text .= OutputString($head); # Generate code to output parameter if( $type =~ /int/ ) { $$output_text .= 'fprintf(output, "%d", ' . $variable . ");\n"; } elsif( $type =~ /long/ ) { $$output_text .= 'fprintf(output, "%ld", ' . $variable . ");\n"; } else { $$output_text .= "fputs($variable.c_str(), output);\n"; } $prototype .= "$type $variable,\n"; $template = $tail; } if( $template ne "" ) { $$output_text .= OutputString($template); } $prototype .= "FILE *output)"; $$output_text = "#include\n" . "#include\n" . "using namespace std;\n" . $prototype . "\n{\n" . $$output_text . "}\n"; $$output_header = "#ifndef WRITE_TEMPLATE_H_\n" . "#define WRITE_TEMPLATE_H_\n" . "#include\n" . "#include\n" . "using namespace std;\n" . "extern $prototype;\n" . "#endif\n"; } # Script entry point unless( $#ARGV >= 1 ) { die "$0 [ ]\n"; } my ($code, $header); LoadInput($ARGV[0], \$code, \$header); for(my $i = 1; $i <= $#ARGV; $i++) { if( $ARGV[$i] =~ /\.h$/ ) { open my $file, ">$ARGV[$i]" or die $!; print $file $header; close $file; } elsif( $ARGV[$i] =~ /\.cc$/ ) { open my $file, ">$ARGV[$i]" or die $!; print $file $code; close $file; } }