More thoroughly parse arguments and defaults

This commit is contained in:
Seth Kingsley 2018-11-14 01:51:12 -08:00
parent e26b91dcd0
commit f61b72728e
2 changed files with 193 additions and 55 deletions

View file

@ -40,7 +40,7 @@ class WrapperClass
when 'Begin', 'BeginChild'
'IsContentVisible'
else
'IsOpen' if m[:type] == 'bool'
'IsOpen' if m[:type] == %w{bool}
end
puts <<EOT
@ -48,7 +48,7 @@ class WrapperClass
#{INDENT}{
EOT
if @state_var
puts "#{INDENT * 2}#{m[:type]} #{@state_var};"
puts "#{INDENT * 2}#{m[:type].join('')} #{@state_var};"
puts
end
end
@ -79,62 +79,157 @@ EOT
end
end
def is_space(token)
token =~ /^\s+/
end
def first_non_space(tokens)
tokens.find { |token| !is_space(token) }
end
def skip_spaces(tokens)
tokens.shift while is_space(tokens.first)
end
def parse_until(tokens, stop_token)
parsed = []
while first_non_space(tokens) != stop_token
token = tokens.shift
fail "End of tokens while looking for #{stop_token}" unless token
parsed << token
end
skip_spaces(tokens)
tokens.shift
parsed
end
def chop_space(tokens)
tokens.pop if is_space(tokens.last)
end
def parse_method(line)
m = {}
tokens = line.scan(/\w+|\s+|\.\.\.|\/\/.*$|./)
skip_spaces(tokens)
return nil unless tokens.shift == 'IMGUI_API'
skip_spaces(tokens)
type_and_name = parse_until(tokens, '(')
m[:name] = type_and_name.pop
chop_space(type_and_name)
m[:type] = type_and_name
return nil unless m[:name].match(/^(Begin|Push|TreeNode)/)
m[:args] = [[]]
m[:argnames] = []
paren_level = 0
in_default = false
loop do
token = tokens.shift
fail "End of tokens while parsing argument list" unless token
case token
when '('
paren_level+= 1
when ')'
break if paren_level == 0
paren_level-= 1
when ','
if paren_level == 0
m[:args] << []
in_default = false
next
end
end
if paren_level == 0 && !in_default
next_non_space = first_non_space(tokens)
if next_non_space == ')' || next_non_space == ',' || next_non_space == '='
m[:argnames] << token
in_default = (next_non_space == '=')
end
end
m[:args][-1] << token
end
m[:attrs] = parse_until(tokens, ';')
skip_spaces(tokens)
m[:rest] = tokens
m
end
current_class = nil
header_file = File.open("../../imgui.h")
header_file.each_line do |line|
break if line.match(/^}\s*\/\/\s*namespace ImGui$/i)
line.match(/^\s*IMGUI_API\s+(?<type>[\w\s]+\w)\s+(?<name>\w*)\((?<args>[^)]+)\)(?<attrs>[^;]*);(?<rest>.*)$/) do |m|
next unless m[:name].match(/^(Begin|Push|Tree)/)
m = parse_method(line)
next unless m
argnames = m[:args].split(/,\s*/).map do |arg|
arg = arg.split(/\s*=\s*/, 2).first
arg.split(/[ \*]/).last
end
next unless m[:name].match(/^(Begin|Push|Tree)/)
# Check for obsolete
if m[:name] == 'Begin' &&
(argnames == %w{name p_open size_on_first_use bg_alpha_override flags} ||
argnames == %w{items_count items_height})
next
end
$stderr.puts m.inspect if $-d
$stderr.puts m.inspect if $-d
fail "Return value #{m[:type]} is not bool or void" unless %w{bool void}.include?(m[:type])
if !current_class || current_class.name != m[:name]
if current_class
current_class.close
puts
end
current_class = WrapperClass.new(m)
end
attrs = m[:attrs].gsub(/IM_FMT(ARGS|LIST)\(\d+\)/) do |a|
a.sub(/\d+/) { |index| (index.to_i + 1).to_s }
end
print "#{INDENT * 2}#{current_class.class_name}(#{m[:args]})#{attrs} { "
use_varargs = false
if argnames.last == '...'
argnames[-1] = 'ap'
use_varargs = true
print "va_list ap; va_start(ap, fmt); "
end
print "#{current_class.state_var} = " if current_class.state_var
print "ImGui::#{m[:name]}"
print 'V' if use_varargs
print "(#{argnames.join(', ')}); "
print 'va_end(ap); ' if use_varargs
puts '}'
# Check for obsolete
if m[:name] == 'Begin' &&
(m[:argnames] == %w{name p_open size_on_first_use bg_alpha_override flags} ||
m[:argnames] == %w{items_count items_height})
next
end
fail "Return value #{m[:type]} is not bool or void" unless [%w{bool}, %w{void}].include?(m[:type])
if !current_class || current_class.name != m[:name]
if current_class
current_class.close
puts
end
current_class = WrapperClass.new(m)
end
shift_index = false
attrs = m[:attrs].map do |attr|
case attr
when /^IM_FMT(ARGS|LIST)$/
shift_index = true
when /^\d+/
if shift_index
attr = (attr.to_i + 1).to_s
shift_index = false
end
end
attr
end.join('')
args = m[:args].map { |argparts| argparts.join('') }.join(',')
print "#{INDENT * 2}#{current_class.class_name}(#{args})#{attrs} { "
use_varargs = false
if m[:argnames].last == '...'
m[:argnames][-1] = 'ap'
use_varargs = true
print "va_list ap; va_start(ap, fmt); "
end
print "#{current_class.state_var} = " if current_class.state_var
print "ImGui::#{m[:name]}"
print 'V' if use_varargs
print "(#{m[:argnames].join(', ')}); "
print 'va_end(ap); ' if use_varargs
puts '}'
end
current_class.close if current_class

View file

@ -26,8 +26,8 @@ namespace ImScoped
{
bool IsContentVisible;
Child(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0) { IsContentVisible = ImGui::BeginChild(str_id, size, 0); }
Child(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0) { IsContentVisible = ImGui::BeginChild(id, size, 0); }
Child(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0) { IsContentVisible = ImGui::BeginChild(str_id, size, border, flags); }
Child(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0) { IsContentVisible = ImGui::BeginChild(id, size, border, flags); }
~Child() { ImGui::EndChild(); }
explicit operator bool() const { return IsContentVisible; }
@ -93,6 +93,14 @@ namespace ImScoped
IMGUI_DELETE_MOVE_COPY(ButtonRepeat);
};
struct Group
{
Group() { ImGui::BeginGroup(); }
~Group() { if () ImGui::EndGroup(); }
IMGUI_DELETE_MOVE_COPY(Group);
};
struct ID
{
ID(const char* str_id) { ImGui::PushID(str_id); }
@ -170,13 +178,28 @@ namespace ImScoped
IMGUI_DELETE_MOVE_COPY(TreeNodeExV);
};
struct TreePush
struct MainMenuBar
{
TreePush(const char* str_id) { ImGui::TreePush(str_id); }
TreePush(const void* ptr_id = NULL) { ImGui::TreePush(ptr_id); }
~TreePush() { ImGui::TreePop(); }
bool IsOpen;
IMGUI_DELETE_MOVE_COPY(TreePush);
MainMenuBar() { IsOpen = ImGui::BeginMainMenuBar(); }
~MainMenuBar() { if (IsOpen) ImGui::EndMainMenuBar(); }
explicit operator bool() const { return IsOpen; }
IMGUI_DELETE_MOVE_COPY(MainMenuBar);
};
struct MenuBar
{
bool IsOpen;
MenuBar() { IsOpen = ImGui::BeginMenuBar(); }
~MenuBar() { if (IsOpen) ImGui::EndMenuBar(); }
explicit operator bool() const { return IsOpen; }
IMGUI_DELETE_MOVE_COPY(MenuBar);
};
struct Menu
@ -191,6 +214,14 @@ namespace ImScoped
IMGUI_DELETE_MOVE_COPY(Menu);
};
struct Tooltip
{
Tooltip() { ImGui::BeginTooltip(); }
~Tooltip() { if () ImGui::EndTooltip(); }
IMGUI_DELETE_MOVE_COPY(Tooltip);
};
struct Popup
{
bool IsOpen;
@ -263,6 +294,18 @@ namespace ImScoped
IMGUI_DELETE_MOVE_COPY(DragDropSource);
};
struct DragDropTarget
{
bool IsOpen;
DragDropTarget() { IsOpen = ImGui::BeginDragDropTarget(); }
~DragDropTarget() { if (IsOpen) ImGui::EndDragDropTarget(); }
explicit operator bool() const { return IsOpen; }
IMGUI_DELETE_MOVE_COPY(DragDropTarget);
};
struct ClipRect
{
ClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) { ImGui::PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); }