Mercurial > hg > octave-thorsten
diff mk-opts.pl @ 3998:f6df65db67f9
[project @ 2002-07-24 18:10:39 by jwe]
author | jwe |
---|---|
date | Wed, 24 Jul 2002 18:10:40 +0000 |
parents | |
children | b4b4515af951 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/mk-opts.pl @@ -0,0 +1,944 @@ +#! /usr/bin/perl + +# Generate option handling code from a simpler input files for +# Octave's functions like lsode, dassl, etc. + +# Input file format: +# +# CLASS = string +# FCN_NAME = string +# DOC_STRING doc END_DOC_STRING +# OPTION +# NAME = string +# TYPE = string +# SET_ARG_TYPE = string (optional, defaults to TYPE) +# INIT_VALUE = string | INIT_BODY code END_INIT_BODY +# SET_EXPR = string | SET_BODY code END_SET_BODY | SET_CODE code END_SET_CODE +# END_OPTION +# +# END_* must appear at beginning of line (whitespace ignored). + +use Getopt::Long; + +$opt_emit_opt_class_header = 0; +$opt_emit_opt_handler_fcns = 0; +$opt_debug = 0; + +GetOptions ("opt-class-header" => \$opt_emit_opt_class_header, + "opt-handler-fcns" => \$opt_emit_opt_handler_fcns, + "debug" => \$opt_debug); + +if (@ARGV == 1) + { + $INFILE = shift @ARGV; + open (INFILE) || die "unable to open input file $INFILE"; + } +else + { + die "usage: mk-opts.pl [options] FILE"; + } + +$opt_num = 0; + +&parse_input; + +&process_data; + +FOO: + { + $opt_emit_opt_class_header && do { &emit_opt_class_header; last FOO; }; + + $opt_emit_opt_handler_fcns && do { &emit_opt_handler_fcns; last FOO; }; + + $opt_debug && do { &emit_options_debug; last FOO; }; + } + +sub parse_input +{ + local ($have_doc_string); + + while (<INFILE>) + { + next if (/^\s*$/); + + if (/^\s*OPTION\s*$/) + { + &parse_option_block; + } + elsif (/^\s*CLASS\s*=\s*"(\w+)"\s*$/) + { + die "duplicate CLASS" if ($class ne ""); + $CLASS = $1; + $class_name = "${CLASS}_options"; + $struct_name = "${class_name}_struct"; + $static_table_name = "${class_name}_table"; + } + elsif (/^\s*FCN_NAME\s*=\s*"(\w+)"\s*$/) + { + die "duplicate FCN_NAME" if ($fcn_name ne ""); + $fcn_name = $1; + } + elsif (/^\s*DOC_STRING\s*$/) + { + die "duplicate DOC_STRING" if ($have_doc_string); + &parse_doc_string; + $have_doc_string = 1; + } + } +} + +sub parse_option_block +{ + local ($have_init_body, $have_set_body, $have_set_code); + + while (<INFILE>) + { + next if (/^\s*$/); + + die "missing END_OPTION" if (/^\s*OPTION\s*$/); + + last if (/^\s*END_OPTION\s*$/); + + if (/^\s*NAME\s*=\s*"(.*)"\s*$/) + { + die "duplicate NAME" if ($name[$opt_num] ne ""); + $name[$opt_num] = $1; + ($opt[$opt_num] = $name[$opt_num]) =~ s/\s+/_/g; + $optvar[$opt_num] = "x_$opt[$opt_num]"; + $kw_tok[$opt_num] = [ split (/\s+/, $name[$opt_num]) ]; + $n_toks[$opt_num] = @{$kw_tok[$opt_num]}; + } + elsif (/^\s*TYPE\s*=\s*"(.*)"\s*$/) + { + die "duplicate TYPE" if ($type[$opt_num] ne ""); + $type[$opt_num] = $1; + } + elsif (/^\s*SET_ARG_TYPE\s*=\s*"(.*)"\s*$/) + { + die "duplicate SET_ARG_TYPE" if ($set_arg_type[$opt_num] ne ""); + $set_arg_type[$opt_num] = $1; + } + elsif (/^\s*INIT_VALUE\s*=\s*"(.*)"\s*$/) + { + die "duplicate INIT_VALUE" if ($init_value[$opt_num] ne ""); + $init_value[$opt_num] = $1; + } + elsif (/^\s*SET_EXPR\s*=\s*"(.*)"\s*$/) + { + die "duplicate SET_EXPR" if ($set_expr[$opt_num] ne ""); + $set_expr[$opt_num] = $1; + } + elsif (/^\s*INIT_BODY\s*$/) + { + die "duplicate INIT_BODY" if ($have_init_body); + &parse_init_body; + $have_init_body = 1; + } + elsif (/^\s*SET_BODY\s*$/) + { + die "duplicate SET_BODY" if ($have_set_body); + &parse_set_body; + $have_set_body = 1; + } + elsif (/^\s*SET_CODE\s*$/) + { + die "duplicate SET_CODE" if ($have_set_code); + &parse_set_code; + $have_set_code = 1; + } + } + + if ($set_arg_type[$opt_num] eq "") + { + $set_arg_type[$opt_num] = $type[$opt_num] + } + else + { + $set_arg_type[$opt_num] + = &substopt ($set_arg_type[$opt_num], $optvar[$opt_num], + $opt[$opt_num], $type[$opt_num]); + } + + $opt_num++; +} + +sub process_data +{ + @uniq_types = &get_uniq_types (@type); + @uniq_set_arg_types = &get_uniq_types (@set_arg_type); + + @get_type_num = &get_uniq_type_num (*type, *uniq_types); + @set_type_num = &get_uniq_type_num (*set_arg_type, *uniq_set_arg_types); + + $max_tokens = &max (@n_toks); + + &get_min_match_len_info ($max_tokens); + + $fcn_name = lc ($CLASS) if ($fcn_name eq ""); + + $opt_fcn_name = "${fcn_name}_options" if ($opt_fcn_name eq ""); + + $static_object_name = "${fcn_name}_opts"; + + if ($doc_string eq "") + { + $doc_string = "When called with two arguments, this function\\n\\ +allows you set options parameters for the function \@code{$fcn_name}.\\n\\ +Given one argument, \@code{$opt_fcn_name} returns the value of the\\n\\ +corresponding option. If no arguments are supplied, the names of all\\n\\ +the available options and their current values are displayed.\\n\\\n"; + } +} + +sub get_uniq_types +{ + local ($k, $i, @retval, %u); + + $k = 0; + + for ($i = 0; $i < $opt_num; $i++) + { + local ($x); + $x = $_[$i]; + $u{$x}++; + $retval[$k++] = $x if ($u{$x} == 1); + } + + @retval; +} + +sub get_uniq_type_num +{ + local (*t, *ut) = @_; + + local ($k, $i, @retval); + + for ($i = 0; $i < $opt_num; $i++) + { + for $k (0 .. $#ut) + { + $retval[$i] = $k if ($t[$i] eq $ut[$k]); + } + } + + @retval; +} + +sub get_min_match_len_info +{ + local ($max_tokens) = @_; + + local ($i, $j, $k); + + for ($i = 0; $i < $opt_num; $i++) + { + for ($j = 0; $j < $max_tokens; $j++) + { + $min_tok_len_to_match[$i][$j] = 0; + } + + $min_toks_to_match[$i] = 1; + + L1: for ($k = 0; $k < $opt_num; $k++) + { + local ($duplicate) = 1; + + if ($i != $k) + { + L2: for ($j = 0; $j < $max_tokens; $j++) + { + if ($j < $n_toks[$i]) + { + if ($kw_tok[$i][$j] eq $kw_tok[$k][$j]) + { + if ($min_tok_len_to_match[$i][$j] == 0) + { + $min_tok_len_to_match[$i][$j] = 1; + } + + $min_toks_to_match[$i]++; + } + else + { + $duplicate = 0; + + if ($min_tok_len_to_match[$i][$j] == 0) + { + $min_tok_len_to_match[$i][$j] = 1; + } + + local (@s) = split (//, $kw_tok[$i][$j]); + local (@t) = split (//, $kw_tok[$k][$j]); + + local ($n, $ii); + $n = scalar (@s); + $n = scalar (@t) if (@t < $n); + + for ($ii = 0; $ii < $n; $ii++) + { + if ("$s[$ii]" eq "$t[$ii]") + { + if ($ii + 2 > $min_tok_len_to_match[$i][$j]) + { + $min_tok_len_to_match[$i][$j]++; + } + } + else + { + last L2; + } + } + + last L1; + } + } + else + { + die "ambiguous options \"$name[$i]\" and \"$name[$k]\"" if ($duplicate); + } + } + } + } + } +} + +sub parse_doc_string +{ + while (<INFILE>) + { + last if (/^\s*END_DOC_STRING\s*$/); + + $doc_string .= $_; + } + + $doc_string =~ s/\n/\\n\\\n/g; +} + +sub parse_init_body +{ + while (<INFILE>) + { + last if (/^\s*END_INIT_BODY\s*$/); + + $init_body[$opt_num] .= $_; + } +} + +sub parse_set_body +{ + while (<INFILE>) + { + last if (/^\s*END_SET_BODY\s*$/); + + $set_body[$opt_num] .= $_; + } +} + +sub parse_set_code +{ + while (<INFILE>) + { + last if (/^\s*END_SET_CODE\s*$/); + + $set_code[$opt_num] .= $_; + } +} + +sub emit_opt_class_header +{ + local ($i, $s); + + print "// DO NOT EDIT! +// Generated automatically from $INFILE. + +#if !defined (octave_${class_name}_h) +#define octave_${class_name}_h 1 + +#include <cfloat> +#include <cmath> + +class +${class_name} +{ +public: + + ${class_name} (void) { init (); } + + ${class_name} (const ${class_name}& opt) { copy (opt); } + + ${class_name}& operator = (const ${class_name}& opt) + { + if (this != &opt) + copy (opt); + + return *this; + } + + ~${class_name} (void) { }\n"; + + print "\n void init (void)\n {\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + if ($init_value[$i]) + { + print " $optvar[$i] = $init_value[$i];\n"; + } + elsif ($init_body[$i]) + { + $s = &substopt ($init_body[$i], $optvar[$i], $opt[$i], $type[$i]); + chop ($s); + $s =~ s/^\s*/ /g; + $s =~ s/\n\s*/\n /g; + print "$s\n"; + } + } + + print " }\n"; + + print "\n void copy (const ${class_name}& opt)\n {\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + print " $optvar[$i] = opt.$optvar[$i];\n"; + } + + print " }\n"; + + print "\n void set_default_options (void) { init (); }\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + if ($set_expr[$i]) + { + &emit_set_decl ($i); + + print "\n { $optvar[$i] = $set_expr[$i]; }\n"; + } + elsif ($set_body[$i]) + { + &emit_set_decl ($i); + + $s = &substopt ($set_body[$i], $optvar[$i], $opt[$i], $type[$i]); + chop ($s); + $s =~ s/^/ /g; + $s =~ s/\n/\n /g; + print "\n {\n$s\n }\n"; + } + elsif ($set_code[$i]) + { + $s = &substopt ($set_code[$i], $optvar[$i], $opt[$i], $type[$i]); + chop ($s); + $s =~ s/^ //g; + $s =~ s/\n /\n/g; + print "\n$s\n"; + } + } + + for ($i = 0; $i < $opt_num; $i++) + { + print " $type[$i] $opt[$i] (void) const\n { return $optvar[$i]; }\n\n"; + } + + print "private:\n\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + print " $type[$i] $optvar[$i];\n"; + } + + print "};\n\n#endif\n"; +} + +sub emit_set_decl +{ + local ($i) = @_; + + print " + void set_$opt[$i] ($set_arg_type[$i] val)"; +} + +sub emit_opt_handler_fcns +{ + local ($i); + + print "// DO NOT EDIT!\n// Generated automatically from $INFILE.\n\n"; + + print "#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <iomanip> +#include <iostream> + +#include \"defun-dld.h\" +#include \"pr-output.h\" + +static ${class_name} ${static_object_name};\n\n"; + + &emit_set_mf_typedefs (@uniq_set_arg_types); + + &emit_get_mf_typedefs (@uniq_types); + + &emit_struct_decl; + + &emit_struct_def; + + &emit_print_function; + + &emit_set_functions; + + &emit_show_function; + + &emit_options_function; +} + +sub emit_set_mf_typedefs +{ + local ($k) = 0; + + foreach (@_) + { + print "typedef void (${class_name}::*set_opt_mf_$k) ($_[$k]);\n"; + $k++; + } + + print "\n"; +} + +sub emit_get_mf_typedefs +{ + local ($k) = 0; + + foreach (@_) + { + print "typedef $_[$k] (${class_name}::*get_opt_mf_$k) (void) const;\n"; + $k++; + } + + print "\n"; +} + +sub emit_struct_decl +{ + local ($i); + + print "#define MAX_TOKENS $max_tokens\n\n"; + + print "struct ${struct_name}\n{\n"; + + print " const char *keyword;\n"; + print " const char *kw_tok[MAX_TOKENS + 1];\n"; + print " int min_len[MAX_TOKENS + 1];\n"; + print " int min_toks_to_match;\n"; + + foreach $i (0 .. $#uniq_set_arg_types) + { + print " set_opt_mf_$i set_fcn_$i;\n"; + } + + foreach $i (0 .. $#uniq_set_arg_types) + { + print " get_opt_mf_$i get_fcn_$i;\n"; + } + + print "};\n\n"; +} + +sub emit_struct_def +{ + local ($i); + + print "#define NUM_OPTIONS $opt_num\n\n"; + + print "static ${struct_name} ${static_table_name} [] =\n{\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + &emit_option_table_entry ($i, 0); + + if ($i < $opt_num - 1) + { + print "\n"; + } + } + + &emit_option_table_entry ($i, 1); + + print "};\n\n"; +} + +sub emit_option_table_entry +{ + local ($i, $empty) = @_; + + local ($k); + + if ($empty) + { + print " { 0,\n"; + } + else + { + print " { \"$name[$i]\",\n"; + } + + local ($n) = scalar $#{$kw_tok[$i]}; + print " {"; + for $k (0 .. $max_tokens) + { + if ($empty || $k > $n) + { + print " 0,"; + } + else + { + print " \"$kw_tok[$i][$k]\","; + } + } + print " },\n"; + + print " {"; + for $k (0 .. $max_tokens) + { + if ($empty || $k > $n) + { + print " 0,"; + } + else + { + print " $min_tok_len_to_match[$i][$k],"; + } + } + print " }, $min_toks_to_match[$i], "; + + print " "; + for $k (0 .. $#uniq_set_arg_types) + { + if ($empty || $k != $set_type_num[$i]) + { + print "0, "; + } + else + { + print "&${class_name}::set_$opt[$i], "; + } + } + + print "\n "; + for $k (0 .. $#uniq_types) + { + if ($empty || $k != $get_type_num[$i]) + { + print "0, "; + } + else + { + print "&${class_name}::$opt[$i], "; + } + } + + print "},\n"; +} + +sub emit_print_function +{ + local ($i); + + print "static void +print_${class_name} (std::ostream& os) +{ + print_usage (\"$opt_fcn_name\", 1); + + os << \"\\n\" + << \"Options for $CLASS include:\\n\\n\" + << \" keyword value\\n\" + << \" ------- -----\\n\"; + + $struct_name *list = $static_table_name;\n\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + print " {\n os << \" \" + << std::setiosflags (std::ios::left) << std::setw (40) + << list[$i].keyword + << std::resetiosflags (std::ios::left) + << \" \";\n\n"; + + if ($type[$i] eq "double") + { + print " double val = $static_object_name.$opt[$i] ();\n\n"; + print " os << val << \"\\n\";\n"; + } + elsif ($type[$i] eq "int") + { + print " int val = $static_object_name.$opt[$i] ();\n\n"; + print " os << val << \"\\n\";\n"; + } + elsif ($type[$i] eq "std::string") + { + print " os << $static_object_name.$opt[$i] () << \"\\n\";\n"; + } + elsif ($type[$i] eq "Array<double>") + { + print " Array<double> val = $static_object_name.$opt[$i] ();\n\n"; + print " if (val.length () == 1) + { + os << val(0) << \"\\n\"; + } + else + { + os << \"\\n\\n\"; + Matrix tmp = Matrix (ColumnVector (val)); + octave_print_internal (os, tmp, false, 2); + os << \"\\n\\n\"; + }\n"; + } + else + { + die ("unknown type $type[$i]"); + } + + print " }\n\n"; + } + + print " os << \"\\n\";\n}\n\n"; +} + +sub emit_set_functions +{ + print "static void +set_${class_name} (const std::string& keyword, const octave_value& val) +{ + $struct_name *list = $static_table_name;\n\n"; + + $iftok = "if"; + + for ($i = 0; $i < $opt_num; $i++) + { + $iftok = "else if" if ($i > 0); + + print " $iftok (keyword_almost_match (list[$i].kw_tok, list[$i].min_len, + keyword, list[$i].min_toks_to_match, MAX_TOKENS)) + {\n"; + + if ($type[$i] eq "double") + { + print " double tmp = val.double_value ();\n\n"; + print " if (! error_state) + $static_object_name.set_$opt[$i] (tmp);\n"; + } + elsif ($type[$i] eq "int") + { + print " int tmp = val.int_value ();\n\n"; + print " if (! error_state) + $static_object_name.set_$opt[$i] (tmp);\n"; + } + elsif ($type[$i] eq "std::string") + { + print " std::string tmp = val.string_value ();\n\n"; + print " if (! error_state) + $static_object_name.set_$opt[$i] (tmp);\n"; + } + elsif ($type[$i] eq "Array<double>") + { + print " Array<double> tmp = val.vector_value ();\n\n"; + print " if (! error_state) + $static_object_name.set_$opt[$i] (tmp);\n"; + } + else + { + die ("unknown type $type[$i]"); + } + + print " }\n"; + } + + print " else + { + warning (\"$opt_fcn_name: no match for `%s'\", keyword.c_str ()); + } +}\n\n"; +} + +sub emit_show_function +{ + local ($i, $iftok); + + print "static octave_value_list +show_${class_name} (const std::string& keyword) +{ + octave_value retval; + + $struct_name *list = $static_table_name;\n\n"; + + $iftok = "if"; + + for ($i = 0; $i < $opt_num; $i++) + { + $iftok = "else if" if ($i > 0); + + print " $iftok (keyword_almost_match (list[$i].kw_tok, list[$i].min_len, + keyword, list[$i].min_toks_to_match, MAX_TOKENS)) + {\n"; + + if ($type[$i] eq "double") + { + print " double val = $static_object_name.$opt[$i] ();\n\n"; + print " retval = val;\n"; + } + elsif ($type[$i] eq "int") + { + print " int val = $static_object_name.$opt[$i] ();\n\n"; + print " retval = static_cast<double> (val);\n"; + } + elsif ($type[$i] eq "std::string") + { + print " retval = $static_object_name.$opt[$i] ();\n"; + } + elsif ($type[$i] eq "Array<double>") + { + print " Array<double> val = $static_object_name.$opt[$i] ();\n\n"; + print " if (val.length () == 1) + { + retval = val(0); + } + else + { + retval = ColumnVector (val); + }\n"; + } + else + { + die ("unknown type $type[$i]"); + } + + print " }\n"; + } + + print " else + { + warning (\"$opt_fcn_name: no match for `%s'\", keyword.c_str ()); + } + + return retval;\n}\n\n"; +} + +sub emit_options_function +{ + print "DEFUN_DLD ($opt_fcn_name, args, , + \"-*- texinfo -*-\\n\\ +\@deftypefn {Loadable Function} {} $opt_fcn_name (\@var{opt}, \@var{val})\\n\\ +$doc_string\@end deftypefn\") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin == 0) + { + print_${class_name} (octave_stdout); + } + else if (nargin == 1 || nargin == 2) + { + std::string keyword = args(0).string_value (); + + if (! error_state) + { + if (nargin == 1) + retval = show_${class_name} (keyword); + else + set_${class_name} (keyword, args(1)); + } + else + error (\"$opt_fcn_name: expecting keyword as first argument\"); + } + else + print_usage (\"$opt_fcn_name\"); + + return retval; +}"; +} + +sub emit_options_debug +{ + print "CLASS = \"$class\"\n"; + + for ($i = 0; $i < $opt_num; $i++) + { + $NAME = $name[$i]; + ($OPT = $NAME) =~ s/\s+/_/g; + $OPTVAR = "x_$OPT"; + $TYPE = $type[$i]; + print "\n"; + print "OPTION\n"; + print " NAME = \"$NAME\"\n"; + print " TYPE = \"$TYPE\"\n"; + if ($set_arg_type[$i]) + { + print eval ("\" SET_ARG_TYPE = \\\"$set_arg_type[$i]\\\"\"") . "\n"; + } + if ($init_value[$i]) + { + print " INIT_VALUE = \"$init_value[$i]\"\n"; + } + if ($init_body[$i]) + { + print " INIT_BODY\n"; + print &substopt ($init_body[$i]); + print " END_INIT_BODY\n"; + } + if ($set_expr[$i]) + { + print " SET_EXPR = \"$set_expr[$i]\"\n"; + } + if ($set_body[$i]) + { + print " SET_BODY\n"; + print &substopt ($set_body[$i]); + print " END_SET_BODY\n"; + } + if ($set_code[$i]) + { + print " SET_CODE\n"; + print &substopt ($set_code[$i]); + print " END_SET_CODE\n"; + } + print "END_OPTION\n"; + } +} + +sub substopt +{ + local ($string, $OPTVAR, $OPT, $TYPE) = @_; + + $string =~ s/\$OPTVAR/$OPTVAR/g; + $string =~ s/\$OPT/$OPT/g; + $string =~ s/\$TYPE/$TYPE/g; + + $string; +} + +sub print_assoc_array +{ + local (%t) = @_; + + local ($k); + + foreach $k (keys (%t)) + { + print "$k: $t{$k}\n"; + } +} + +sub max +{ + local ($max) = shift; + + foreach (@_) + { + $max = $_ if $max < $_; + } + + $max; +}