mirror of
https://github.com/ksherlock/gopher.git
synced 2025-01-17 11:30:05 +00:00
new options generator
This commit is contained in:
parent
4e96ff9651
commit
86f0933ec7
413
options.rb
Normal file
413
options.rb
Normal file
@ -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 = <<EOD
|
||||
#ifndef __<%= config[:prefix] %>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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user