From 86f0933ec7a92139a4688f4f0d02cec631ec5549 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 11 Aug 2013 12:51:45 -0400 Subject: [PATCH] new options generator --- options.rb | 413 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 options.rb diff --git a/options.rb b/options.rb new file mode 100644 index 0000000..e8e0a1d --- /dev/null +++ b/options.rb @@ -0,0 +1,413 @@ +#!/bin/env ruby -w + +require 'erb' +require 'optparse' + +class Option + + attr_reader :name + attr_reader :description + attr_reader :argument # nil, :, =, ? + + + # name of the field, in the struct + # will be null if virtual. + attr_reader :field + + # array of custom code. + attr_accessor :code + + attr_reader :modifiers + + + def _fieldName(name, modifiers) + return nil if modifiers[:virtual] + return modifiers[:field] if modifiers[:field] + return "_#{name}" + end + + def initialize(name, argument, modifiers) + @code = [] + + modifiers ||= {} + @name = name + @field = _fieldName(name, modifiers) + @argument = argument + @modifiers = modifiers + end + + def to_s() + return "Option: #{description}#{argument} #{field}" + end + + # def addCode(x) + # @code.push(x) + # end + + # def finishCode + # # remove all blank lines at the end + # while @code.length && @code.last =~ /^\s*$/ + # @code.pop + # end + # end + +end + +class ShortOption < Option + def initialize(name, argument, modifiers) + super(name, argument, modifiers) + + @description = "-#{name}" + end + + + def generateCase() + + rv = [] + indent = " " * 8 + rv.push indent, "case '#{@name}':\n" + indent += " " + + if (@argument) + #print indent, "GETOPTARG(#{@description})\n" + rv.push indent, "++j;\n" + rv.push indent, "if (cp[j]) {\n" + rv.push indent, " optarg = cp + j;\n" + rv.push indent, "} else {\n" + rv.push indent, " ++i;\n" + rv.push indent, " if (i < argc) optarg = argv[i];\n" + rv.push indent, "}\n" + rv.push indent, "if (!optarg) {\n" + rv.push indent, " fputs(\"#{@description} requires an argument\\n\", stderr);\n" + rv.push indent, " exit(1);\n" + rv.push indent, "}\n" + end + + if @field + if @argument + rv.push indent, "options->#{@field} = optarg;\n" + else + if @modifiers[:increment] + rv.push indent, "options->#{@field} += 1;\n" + else + rv.push indent, "options->#{@field} = 1;\n" + end + end + end + + @code.each {|x| rv.push indent, x } + rv.push indent,"break;\n" + + end + + def generateField() + # generate the field for the struct. + + return "" unless @field + return " char *#{@field};\n" if @argument + return " unsigned #{@field};\n" if @modifiers[:increment] + return " unsigned #{@field}:1;\n" + end +end + + +def arrayTrim(x) + while x.length && x.last =~ /^\s*$/ + x.pop + end + x +end + +def parseModifiers(string) + + # splits a key=value (, key=value)* string + # key is equivalent to key={true} + rv = {} + return rv unless string + args = string.split(',') + args.each{|x| + x.strip! + next unless x.length + + key, value = x.split('=') + + value ||= true # key == key={true} + key = key.intern + + rv[key] = value + } + + return rv; +end + + +@shortRE = / + ^ + -([\w]) # 1. letter + ([:])? # 2. argument type? + \s* + (?:\[ + ([\w\s,=]*) # 3. modifiers? + \])? + \s* + (->)? # 4. code? + $ + /x + +@longRE = / + ^ + --([\w-]*) # 1. name + ([=?])? # 2. argument type? + \s* + (?:\[ + ([\w\s,=]*) # 3. modifiers? + \])? + \s* + (->)? # 4. code? + $ + /x + +@headerTemplate = <Options__ +#define __<%= config[:prefix] %>Options__ + +typedef struct <%= config[:prefix] %>Options +{ +<%= (config[:extra_fields] || []).join() %> +<%= (options.map {|x| x.generateField }).join() %> +} <%= config[:prefix] %>Options; + +int Get<%= config[:prefix] %>Options(int argc, char **argv, + <%= config[:prefix] %>Options *options); +#endif +EOD +# + + +def stripExtension(path) + return $1 if path =~ /^(.*?)\.([^.\/]*)$/ + return path +end + +def process(infile, keepName) + + opt = nil # current option being processed + tmp = [] # code block array + callback = nil # callback when code is finished. + + + options = [] # list of options + config = {} + + infile.each_line { |line| + + line.chomp! + + line.rstrip! + + if callback + if line == "" || line =~ /^\s+/ + tmp.push(line + "\n") + next + end + # store... + + callback.call(arrayTrim(tmp)); + callback = nil + tmp = [] + code = false + end + + next if line =~ /^\s*#/ + + if line =~ /^%/ + + if m = line.match(/^%(\w+)=(\w+)$/) + key = m[1].intern + config[key] = m[2] + next + end + + if m = line.match(/^%(\w+)$/) + key = m[1].intern + config[key] = true + next + end + + if m = line.match(/^%(\w+)\s*->$/) + key = m[1].intern + callback = Proc.new {|code| config[key] = code } + + next + end + + $stderr.puts "Not supported: #{line}" + next + end + + if m = line.match(@shortRE) + tmp = [] + flag = m[1] + arg = m[2] + modifiers = m[3] + modifiers = parseModifiers(modifiers) + opt = ShortOption.new(flag, arg, modifiers) + options.push(opt) + + # any code? + if m[4] + callback = Proc.new {|code| opt.code = code } + end + + end + + + # + } + + # if it ended within a code block... + if callback + callback.call(arrayTrim(tmp)) + callback = nil + tmp = [] + end + + options.sort! {|a, b| a.name <=> b.name } + + + keepName = nil if keepName == "" + keepBase = stripExtension(keepName) + + headerName = (config[:prefix] || '') + 'Options.h' + headerName = keepBase + '.h' if keepBase + + io = $stdout + io = File.open(keepName, "w") if keepName + + b = binding + erb = ERB.new(DATA.read(), 0, "%<>") + + io.write(erb.result(b)) + io.close unless io == $stdout + + + # header file. + io = $stdout + io = File.open(headerName, "w") if headerName && keepName + erb = ERB.new(@headerTemplate, 0, "%<>") + io.write(erb.result(b)) + io.close unless io == $stdout + +end + +keepFile = nil + +op = OptionParser.new {|opts| + opts.banner = "Usage: options.rb [options] [infile]" + + opts.on("-o", "--keep OUTFILE", "Specify output file name") do |filename| + keepFile = filename + end +} + +op.parse! + + +keepFile = nil if keepFile == "" + +case ARGV.length +when 0 + process($stdin, keepFile) +when 1 + file = ARGV[0] + io = $stdin + if file != '-' + keepFile = stripExtension(file) + '.c' if keepFile == nil + io = File.open(file, "r") + end + process(io, keepFile) + io.close unless io == $stdin +else + op.help + exit(1) +end + + + +exit(0) + +__END__ +#ifdef __ORCAC__ +#pragma optimize 79 +#pragma noroot +#endif + +#include +#include + +#include "<%= File.basename(headerName) %>" + +<%= (config[:extra_includes] || []).join() %> + +int Get<%= config[:prefix] %>Options(int argc, char **argv, + struct <%= config[:prefix] %>Options *options) +{ + int i, j; + int eof = 0; + int mindex = 1; /* mutation index */ + + for (i = 1; i < argc; ++i) + { + char *cp = argv[i]; + char c = cp[0]; + + <% if config[:posixly_correct] %> + // stop processing at first non-opt. + if (c != '-') + { + eof = 1; + if (i == mindex) return argc; + argv[mindex] = argv[i]; + ++mindex; + continue; + } + <% end %> + + if (eof || c != '-') + { + if (mindex != i) argv[mindex] = argv[i]; + ++mindex; + continue; + } + + // long opt check would go here... + if (cp[1] == '-' && cp[2] == 0) + { + eof = 1; + continue; + } + + // special case for '-' + j = 0; + if (cp[1] != 0) j = 1; + + for (; ; ++j) + { + char *optarg = 0; + + c = cp[j]; + if (!c) break; + switch(c) + { +<%= options.map{|o| o.generateCase() }.join() %> + default: + fprintf(stderr, "-%c : invalid option\n", c); + exit(1); + break; + } + // could optimize out if no options have flags. + if (optarg) break; + } + } + return mindex; +}