-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathglewby.rb
220 lines (180 loc) · 5.65 KB
/
glewby.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/usr/bin/env ruby
require 'erb'
require 'getoptlong'
IDENTIFIER = '[A-Za-z_][A-Za-z_0-9]*'
TYPE = '(?:const\s+)?' + IDENTIFIER + '(?:\s*(?:const\s+)?\s*\*)*'
ARG = TYPE + '\s*(?:' + IDENTIFIER + ')?(?:\s*\[\d*\])*'
ARGS = '(?:' + ARG + ')?(?:\s*,\s*' + ARG + ')*'
FUNCTION = 'GLAPI\s+(' + TYPE + ')\s+GLAPIENTRY\s+gl(' + IDENTIFIER + ')\s*\(\s*(' + ARGS + ')\s*\)\s*;'
FUNCTYPE = 'typedef\s+(' + TYPE + ')\s+\(GLAPIENTRY\s*\*\s*PFNGL(' + IDENTIFIER + ')PROC\)\s*\(\s*(' + ARGS + ')\s*\);'
ARG_CAPTURE = '(' + TYPE + ')\s*(' + IDENTIFIER + ')?((?:\s*\[\d*\])*)'
Function = Struct.new('Function', :name, :return_type, :args)
Argument = Struct.new('Argument', :name, :type)
class DevNull
def puts(*args)
end
end
class Type
def initialize(const)
@const = const
end
def to_s
if @const then
' const'
else
''
end
end
def const?
@const
end
def == (other)
@const == other.const?
end
end
class SimpleType < Type
attr_reader :name
def initialize(name, const)
super(const)
@name = name
end
def to_s
@name + super
end
def freeable_copy
self.class.new(@name, false)
end
def void?
@name == 'void' || @name == 'GLvoid'
end
def pointer?
false
end
def == (other)
@name == other.name && super(other)
end
end
class PointerType < Type
attr_reader :type
def initialize(type, const)
super(const)
@type = type
end
def to_s
@type.to_s + ' *' + super
end
def freeable_copy
self.class.new(@type.freeable_copy, false)
end
def void?
false
end
def pointer?
true
end
def == (other)
@type == other.type && super(other)
end
end
def parse_type(type)
bits = type.split(/\b/).collect do |token|
s = token.strip
if s.size == 0 then
nil
else
s
end
end.compact
# TODO there's gotta be a better way to do this!
if bits.size == 1 then
SimpleType.new(bits[0], false)
elsif bits.size == 2 && bits[0] == 'const' then
SimpleType.new(bits[1], true)
elsif bits.size == 2 && bits[1] == '*' then
PointerType.new(SimpleType.new(bits[0], false), false)
elsif bits.size == 2 && bits[1] =~ /\*\s*\*/ then
PointerType.new(PointerType.new(SimpleType.new(bits[0], false), false), false)
elsif bits.size == 3 && bits[0] == 'const' && bits[2] == '*' then
PointerType.new(SimpleType.new(bits[1], true), false)
elsif bits.size == 3 && bits[0] == 'const' && bits[2] =~ /\*\s*\*/ then
PointerType.new(PointerType.new(SimpleType.new(bits[1], true), false), false)
elsif bits.size == 5 && bits[0] == 'const' && bits[2] == '*' && bits[3] == 'const' && bits[4] == '*' then
PointerType.new(PointerType.new(SimpleType.new(bits[1], true), true), false)
else
p bits
exit
end
end
def parse_args(args)
if args =~ /^\s*void\s*$/ then
return []
end
extra_arg_index = -1
args = args.split(/,/)
args.collect do |arg|
raise "Can't parse argument " + arg unless arg =~ Regexp.compile(ARG_CAPTURE)
name = $2 || 'arg' + (extra_arg_index += 1).to_s
raw_type = ($1 + $3).gsub(/\[\d*\]/, '*').strip
type = parse_type(raw_type)
Argument.new(name, type)
end
end
def parse_header(path, rejects)
extensions = {}
constants = {}
functions = {}
functypes = {}
File.open(path) do |header|
header.each_line do |line|
if line =~ /^#define\s+GL_([A-Z_0-9]+)\s+.*$/ then
constants[$1] = true
elsif line =~ Regexp.compile(FUNCTION) then
functions[$2] = Function.new($2, parse_type($1), parse_args($3))
elsif line =~ Regexp.compile(FUNCTYPE) then
functypes[$2] = Function.new($2, parse_type($1), parse_args($3))
elsif line =~ /^#define\s+gl([A-Z][A-Za-z0-9]+)\s+GLEW_GET_FUN.*$/ then
fn = functypes[$1.upcase]
if fn == nil then
puts("no type for #{$1}")
else
functions[$1] = Function.new($1, fn.return_type, fn.args)
end
elsif line =~ /^#define\s+GLEW_([A-Z][A-Za-z0-9_]+)\s*GLEW_GET_VAR.*$/ then
extensions[$1] = true
else
rejects.puts(line)
end
end
end
return extensions, constants, functions
end
def main(opts)
glew_header = nil
template_dir = nil
output_dir = nil
opts.each do |opt, arg|
case opt
when '--glew-header'
glew_header = arg
when '--template-directory'
template_dir = arg
when '--output-directory'
output_dir = arg
end
end
extensions, constants, functions =
parse_header(glew_header, DevNull.new)
Dir[template_dir + '/*.r*'].each do |template_file_path|
template = ERB.new(File.read(template_file_path))
basename = File.basename(template_file_path)
new_basename = basename.gsub(/\.r/, '.')
output_path = output_dir + '/' + new_basename
File.open(output_path, 'wb') do |file|
file.write(template.result(binding))
end
end
end
main(GetoptLong.new(
[ '--glew-header', GetoptLong::REQUIRED_ARGUMENT ],
[ '--template-directory', GetoptLong::REQUIRED_ARGUMENT ],
[ '--output-directory', GetoptLong::REQUIRED_ARGUMENT ]))