diff options
| -rw-r--r-- | Readme.mdown | 62 | ||||
| -rw-r--r-- | generate.rb | 189 | ||||
| -rw-r--r-- | rework.mdown | 62 | ||||
| -rw-r--r-- | templates.rb | 415 |
4 files changed, 565 insertions, 163 deletions
diff --git a/Readme.mdown b/Readme.mdown index bfda7ce..8ca731b 100644 --- a/Readme.mdown +++ b/Readme.mdown @@ -10,7 +10,7 @@ A binding assistant and generator for C/C++ to mruby(Under heavy WIP) ### How I plan for it to work: -1. Run the scanner which will generate a glue.json file. This json file will contain all functions(and their params) as well as all structs(and their params) +1. Run the scanner which will generate a func.json file. This json file will contain all functions(and their params) as well as all structs(and their params) 2. Create a configuration file where you can reference specific functions and how you want their bindings to be generated differently - for example, under what module or class a function should belong - if a certain param should use self instead of passing in something @@ -18,28 +18,38 @@ A binding assistant and generator for C/C++ to mruby(Under heavy WIP) - insert bindings you made yourself 3. Run the generator with the configuration file - this generates the resulting binding code -#### Todo: - -- [X] parse C files for function and struct declarations -- [X] plan for DSL for configuration file -- [ ] create generator's default output - - [X] phase 1 - bind returnless, paramless functions - - [X] phase 2 - bind standard type return functions(e.g string or int), but still paramless - - bool - - int - - float - - double - - string - - [X] phase 3 - bind standard type return or params - - [X] phase 4 - bind struct construction(returning struct objects) - - [X] returning structs - - [X] accessors for values inside structs - - [X] bind struct initializer - - [ ] phase 5 - bind remaining struct related things - - [ ] struct as params of functions - - [ ] fix structs inside of structs - - [ ] for initilization - - [ ] for accessors -- [ ] clean up code -- [ ] have generator use config DSL file to customize bindings -- [ ] do it all again for C++ +### Opinionated Bindings + +The defaults of FelBind make some assumptions on how you would like the resulting interface to look like. + +- Functions get bound to methods using snake_case and kwargs + - `SomeFunction(someValue)` => `some_function(some_value: thing)` +- Functions are defined under a module(by default `Test`) + - `Test.some_function` +- Structs are defined as classes under a module(by default `Test`) + - `Test::MyStruct` +- Functions beginning with `Set` get bound as a method with `=` + - `SetSomeFunction(value)` => `some_function = value` +- Functions beginning with `Get` get bound as a method without it + - `GetSomeFunction()` => `some_function` + +### What Currently Works: + +- Wrapping functions that return or have parameters that are of the basic C types(int, float, char \*, etc) or their pointers(int \*, float \*, etc[except char *]) +- Wrapping function that return or have parameters that are structs or their pointers +- Wrapping structs +- Giving struct objects initializers and accessers + +### What Doesn't Work: + +- Binding C functions that begin with `Set` are bound incorrectly +- Making accessors for structs that contain structs +- kwargs should follow snake_case naming conventions +- The config system +- Struct Aliases(might make this manually done) + +### What isnt currently planned to make work: + +- Functions that utilize the `* void` type +- Nested Pointers +- Functions with variable arguments diff --git a/generate.rb b/generate.rb index df84164..93545d1 100644 --- a/generate.rb +++ b/generate.rb @@ -20,7 +20,7 @@ options[:glue] ||= './glue.json' glue = JSON.parse(File.read(options[:glue])) # configuration -Tplt.treated_as_int |= ['unsigned char'] +#Template.treated_as_int |= ['unsigned char'] LibraryName = 'Test' $phase1 = {} @@ -34,8 +34,8 @@ $complete_phase3 = {} $complete_phase4 = {} $complete_phase5 = {} -result = "" -includes = %{ +$result = "" +$includes = %{ #include <raylib.h> #include <mruby.h> #include <mruby/array.h> @@ -46,8 +46,11 @@ includes = %{ #include <mruby/compile.h> #include <stdlib.h> } -defines = "" -init_body = "" + + +$defines = "" +$init_body = "" +Template.parse_struct_types(glue.last) # convert types @@ -64,22 +67,35 @@ glue.first.keys.each do |k| end end +glue.first.each do |params| + params[1].map! do |param| + rpart = param.rpartition(' ') + + if ['Texture2D'].include? rpart.first + "Texture #{rpart.last}" + elsif ['RenderTexture2D'].include? rpart.first + "RenderTexture #{rpart.last}" + else + param + end + end +end # for displaying statistics glue.first.each do |func, params| if (func.rpartition(' ').first == 'void') && (params[0] == 'void') $phase1[func] = params - elsif (Tplt.non_struct_types.include? func.rpartition(' ').first) && (params[0] == 'void') + elsif (Template.non_struct_types =~ func.rpartition(' ').first) && (params[0] == 'void') $phase2[func] = params else no_struct_param = true params.each do |param| - if !(Tplt.non_struct_types.include? param.rpartition(' ').first) + if !(Template.non_struct_types =~ param.rpartition(' ').first) no_struct_param = false break end end if no_struct_param - if Tplt.non_struct_types.include? func.rpartition(' ').first + if Template.non_struct_types =~ func.rpartition(' ').first $phase3[func] = params else $phase4[func] = params @@ -89,7 +105,6 @@ glue.first.each do |func, params| end end end - # also for display statistics def debug_mark_binding(func, params) if $phase1.include? func @@ -105,13 +120,12 @@ def debug_mark_binding(func, params) end end - $all_params = [] $bound_params = [] # generates structs, accessors, and initializers glue.last.each do |struct, params| - defines += Tplt.init_struct_wrapper(struct) - init_body += Tplt.init_class(struct, LibraryName.downcase) + $defines += Template.init_struct_wrapper(struct) + $init_body += Template.init_class(struct, LibraryName.downcase) init_vars = '' @@ -121,17 +135,17 @@ glue.last.each do |struct, params| param_datatype = rpart.first param_name = rpart.last - next unless Tplt.non_struct_types.include? param_datatype + next unless Template.non_struct_types =~ param_datatype $bound_params.push param # getter # take no params # unwrap struct # return(using correct type conversion) - body = Tplt.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) - body += "return #{Tplt.to_mrb(param_datatype, "struct_#{struct.downcase}->#{param_name}")};\n" - defines += Tplt.function("#{struct}_get_#{param_name}", body) - init_body += Tplt.init_function("#{struct.downcase}_class", param_name, "#{struct}_get_#{param_name}", "MRB_ARGS_NONE()") + body = Template.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) + body += "return #{Template.to_mrb(param_datatype, "struct_#{struct.downcase}->#{param_name}")};\n" + $defines += Template.function("#{struct}_get_#{param_name}", body) + $init_body += Template.init_function("#{struct.downcase}_class", param_name, "#{struct}_get_#{param_name}", "MRB_ARGS_NONE()") # setter # init var of correct type @@ -139,25 +153,25 @@ glue.last.each do |struct, params| # unwrap struct # set value in struct # return same value - body = Tplt.get_args({ "#{param_name}": "#{param_datatype}" }) - body += Tplt.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) - body += "struct_#{struct.downcase}->#{param_name} = #{param_name};\n" - body += "return #{Tplt.to_mrb(param_datatype, param_name)};\n" - defines += Tplt.function("#{struct}_set_#{param_name}", body) - init_body += Tplt.init_function("#{struct.downcase}_class", "#{param_name}=", "#{struct}_set_#{param_name}", "MRB_ARGS_REQ(1)") + body = Template.get_args({ "#{param_name}": "#{param_datatype}" }) + body += Template.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) + body += "struct_#{struct.downcase}->#{param_name} = #{Template::C.convention_parameter(param_name)};\n" + body += "return #{Template.to_mrb(param_datatype, Template::C.convention_parameter(param_name))};\n" + $defines += Template.function("#{struct}_set_#{param_name}", body) + $init_body += Template.init_function("#{struct.downcase}_class", "#{param_name}=", "#{struct}_set_#{param_name}", "MRB_ARGS_REQ(1)") end - # initializer + ## initializer # init the struct(using mrb to allocate) # get values # assign values to struct # wrap struct # return self body = '' - body += Tplt.get_module(LibraryName) - body += Tplt.get_class(struct, LibraryName.downcase) + body += Template.get_module(LibraryName) + body += Template.get_class(struct, LibraryName.downcase) body += "#{struct} *wrapped_value = (#{struct} *)mrb_malloc(mrb, sizeof(#{struct}));\n" #body += "*wrapped_value = {0};\n" #{func_name}(" @@ -172,8 +186,8 @@ glue.last.each do |struct, params| #init_var_body += temp + ";\n" init_array_body += "mrb_intern_lit(mrb, \"#{temp_rpart.last}\"),\n" #unwrapped_kwargs += Tplt.unwrap_kwarg(index, "#{temp_rpart.last} = #{Tplt.to_c(temp_rpart.first, "kw_values[#{index}]")};", nil, "#{temp_rpart.last} Argument Missing") - if Tplt.non_struct_types.include? temp_rpart.first - unwrapped_kwargs += Tplt.unwrap_kwarg(index, "wrapped_value->#{temp_rpart.last} = #{Tplt.to_c(temp_rpart.first, "kw_values[#{index}]")};\n") + if Template.non_struct_types =~ temp_rpart.first + unwrapped_kwargs += Template::C.unwrap_kwarg(index, "wrapped_value->#{temp_rpart.last} = #{Template.to_c(temp_rpart.first, "kw_values[#{index}]")};\n") else # this is for structs or "undetermined" types # doesnt work yet @@ -181,13 +195,13 @@ glue.last.each do |struct, params| #unwrapped_kwargs += Tplt.unwrap_kwarg(index, "wrapped_value->#{temp_rpart.last} = (#{temp_rpart.first})kw_values[#{index}];\n") end end - body += Tplt.get_kwargs(params.length, '', init_array_body) + body += Template::C.get_kwargs(params)#params.length, '', init_array_body) body += unwrapped_kwargs body += "mrb_data_init(self, wrapped_value, &mrb_#{struct}_struct);\n" body += 'return self;' - defines += Tplt.function("#{struct}_initialize", body) - init_body += Tplt.init_function("#{struct.downcase}_class", "initialize", "#{struct}_initialize", "MRB_ARGS_OPT(1)") + $defines += Template.function("#{struct}_initialize", body) + $init_body += Template.init_function("#{struct.downcase}_class", "initialize", "#{struct}_initialize", "MRB_ARGS_OPT(1)") end @@ -202,20 +216,67 @@ glue.first.each do |func, params| func_datatype = rpart.first func_name = rpart.last - # TODO: just treat longs and shorts as ints - # + # TODO make a skip detector(what functions to skip bindings) + skip = false + #puts "FUNCTION" + #puts "#{func.rpartition(' ').first}| + |#{func.rpartition(' ').last}" + params.each do |param| + #puts "#{param.rpartition(' ').first}| - |#{param.rpartition(' ').last}" + unless (Template.all_valid_types =~ param.rpartition(' ').first) || ("void" == param) + skip = true + break + end + #if param.chars.include? '*' + # unless /^char \*$/ =~ param.rpartition(' ').first + # skip = true + # break + # end + #end + end + next if skip + #next if ['SetTraceLogCallback', 'SetSaveFileTextCallback', 'SetSaveFileDataCallback', 'SetLoadFileTextCallback', 'SetLoadFileDataCallback', 'SetCameraMode', 'GetWorldToScreenEx', 'GetWorldToScreen', 'GetMouseRay', 'GetCameraMatrix', 'DrawBillboardRec', 'DrawBillboardPro', 'DrawBillboard'].include? func_name + # since void * can be anything just skip functions # (by default) that use it next if ['void *'].include? func_datatype + unless Template.all_valid_types =~ func_datatype + puts "// \"#{func_datatype}\" is not a function return datatype that can be currently autobound. From function: \"#{func_name}\"\n\n" + next + end + + body = '' + + body += Template::C.initialize_variables(params, glue.last, func_name) + + body += Template::C.get_kwargs(params) + + body += Template::C.parse_kwargs(params) + body += "\n" # formatting + + body += Template::C.initialize_return_var(func_datatype, func_name) + body += "\n" # formatting + + body += Template.format_set_method_call(func_datatype, func_name, params, Template.struct_types =~ func_datatype.gsub(/ *\*+$/,'')) + body += Template.format_return(func_datatype, func_name) + $defines += "\n//#{func}" + $defines += Template.function(func_name, body) + $init_body += Template.init_module_function(LibraryName.downcase, Template::MRuby.rubify_func_name(func_name), func_name, "MRB_ARGS_OPT(1)") + + debug_mark_binding(func, params) + #puts body + # TODO CONTINUE HERE + #puts "// --- NEXT ---" + #next +=begin # if phase 1 or 2 if (func_datatype == 'void' && params[0] == 'void') || ((Tplt.non_struct_types.include? func_datatype) && (params[0] == 'void')) body = Tplt.return_format(func, params) - #defines += 'PHASE 1\n' - defines += "\n//#{func}" - defines += Tplt.function(func_name, body) - init_body += Tplt.init_module_function(LibraryName.downcase, Tplt.rubify_func_name(func_name), func_name, "MRB_ARGS_NONE()") + #$defines += 'PHASE 1\n' + $defines += "\n//#{func}" + $defines += Tplt.function(func_name, body) + $init_body += Tplt.init_module_function(LibraryName.downcase, Tplt.rubify_func_name(func_name), func_name, "MRB_ARGS_NONE()") debug_mark_binding(func, params) else Tplt.non_struct_types.include? func_datatype # accept params @@ -272,9 +333,9 @@ glue.first.each do |func, params| body += "return mrb_obj_value(Data_Wrap_Struct(mrb, #{func_datatype.downcase}_mrb_class, &mrb_#{func_datatype}_struct, wrapped_value));" end - defines += "\n//#{func}" - defines += Tplt.function(func_name, body) - init_body += Tplt.init_module_function(LibraryName.downcase, Tplt.rubify_func_name(func_name), func_name, "MRB_ARGS_OPT(1)") # opt stuff isnt correct, need to look at this again + $defines += "\n//#{func}" + $defines += Tplt.function(func_name, body) + $init_body += Tplt.init_module_function(LibraryName.downcase, Tplt.rubify_func_name(func_name), func_name, "MRB_ARGS_OPT(1)") # opt stuff isnt correct, need to look at this again # --- #puts func debug_mark_binding(func, params) @@ -284,27 +345,45 @@ glue.first.each do |func, params| end end end +raise 'end of testing' +=end +end +$init_body.prepend(Template.define_module(LibraryName)) -init_body.prepend(Tplt.define_module(LibraryName)) - -result = %{ -#{includes} -#{defines} -#{Tplt.base(LibraryName.downcase, init_body, nil)} +$result = %{ +#{$includes} +#{$defines} +#{Template.base(LibraryName.downcase, $init_body, nil)} } -result += "//Bound Functions: #{$complete_phase1.length + $complete_phase2.length + $complete_phase3.length + $complete_phase4.length + $complete_phase5.length} / #{$phase1.length + $phase2.length + $phase3.length + $phase4.length + $phase5.length}\n//---\n" +$result += "//Bound Functions: #{$complete_phase1.length + $complete_phase2.length + $complete_phase3.length + $complete_phase4.length + $complete_phase5.length} / #{$phase1.length + $phase2.length + $phase3.length + $phase4.length + $phase5.length}\n//---\n" + +$result += "//Phase 1 Functions: #{$complete_phase1.length} / #{$phase1.length}\n" +$result += "//Phase 2 Functions: #{$complete_phase2.length} / #{$phase2.length}\n" +$result += "//Phase 3 Functions: #{$complete_phase3.length} / #{$phase3.length}\n" +$result += "//Phase 4 Functions: #{$complete_phase4.length} / #{$phase4.length}\n" +$result += "//Phase 5 Functions: #{$complete_phase5.length} / #{$phase5.length}\n" +$result += "\n" +#$result += "//Struct Accessors: #{$bound_params.length} / #{$all_params.length}\n" -result += "//Phase 1 Functions: #{$complete_phase1.length} / #{$phase1.length}\n" -result += "//Phase 2 Functions: #{$complete_phase2.length} / #{$phase2.length}\n" -result += "//Phase 3 Functions: #{$complete_phase3.length} / #{$phase3.length}\n" -result += "//Phase 4 Functions: #{$complete_phase4.length} / #{$phase4.length}\n" -result += "//Phase 5 Functions: #{$complete_phase5.length} / #{$phase5.length}\n" -result += "\n" -result += "//Struct Accessors: #{$bound_params.length} / #{$all_params.length}\n" +#pp ($phase3.keys - $complete_phase3.keys) +#puts +#pp $complete_phase3 +all_completed = $complete_phase1.keys | $complete_phase2.keys | $complete_phase3.keys | $complete_phase4.keys | $complete_phase5.keys +all = $phase1.keys | $phase2.keys | $phase3.keys | $phase4.keys | $phase5.keys + +$result += "/* Unbound:\n" +(all - all_completed).each do |unbound| + $result += "#{unbound}\n" +end +$result += "*/\n" + +($phase4.keys - $complete_phase4.keys).each do |ye| + #puts ye +end -puts result +puts $result #$phase4.reverse_each do |key, elem| # puts '---' diff --git a/rework.mdown b/rework.mdown new file mode 100644 index 0000000..60fa67e --- /dev/null +++ b/rework.mdown @@ -0,0 +1,62 @@ +# Refactoring Document + +## Types of functions +- C function wrap +- getter +- setter +- initializer + +### C function wrap +``` +static mrb_value +mrb_#{function_name}(mrb_state* mrb, mrb_value self) { + + #{initialize vars} + + #{unwrap kwarg/arg} + + #{kwargs.each do assignment} <-- this will need to unwrap structs + ^ also need to check if a default exists + + #{call method} + + #{wrap return struct} <-- only if return is new struct + #{return} +} +``` + +### Getter +``` +mrb_#{struct_name}_get_#{var}(mrb_state* mrb, mrb_value self) { + +#{unwrap struct} + +#{return value} <-- may need to wrap value if its a struct +} +``` + +### Setter +``` +mrb_#{struct_name}_set_#{var}(mrb_state* mrb, mrb_value self) { +#{initialize var} +#{get arg} +#{unwrap struct} +#{set value} +#{return the value from the struct} +} +``` + +### Initializer +``` +mrb_#{struct_name}_initialize(mrb_state* mrb, mrb_value self) { +#{initialize var} +#{unwrap kwarg/arg} + +#{kwargs.each do assignment} <-- this will need to unwrap structs +^ also need to check if a default exists + +#{initialize as mrb object} + +return self +} +``` diff --git a/templates.rb b/templates.rb index b4ca43c..1846a0e 100644 --- a/templates.rb +++ b/templates.rb @@ -1,46 +1,317 @@ -module Tplt # Template - class << self - def base(gem_name, init_body, final_body) - %{ -void -mrb_mruby_#{gem_name}_gem_init(mrb_state* mrb) { -#{init_body} -} +module Template # Template + # methods that convert something from ruby-land to c-land + module C + class << self + def to_c_function_name(function_name:) + "mrb_#{function_name}" + end -void -mrb_mruby_#{gem_name}_gem_final(mrb_state* mrb) { -#{final_body} + def to_getter_name(struct_name:, variable_name:) + "mrb_#{struct_name}_get_#{variable_name}" + end + + def to_setter_name(struct_name:, variable_name:) + "mrb_#{struct_name}_set_#{variable_name}" + end + + def to_initializer_name(struct_name:) + "mrb_#{struct_name}_initialize" + end + + def format_type(param_datatype) + if Template.treated_as_int =~ param_datatype + 'int' + elsif Template.treated_as_int_pointer =~ param_datatype + 'int' + elsif Template.treated_as_bool =~ param_datatype + 'bool' + elsif Template.treated_as_bool_pointer =~ param_datatype + 'bool' + elsif Template.treated_as_float =~ param_datatype + 'float' + elsif Template.treated_as_float_pointer =~ param_datatype + 'float' + elsif Template.treated_as_string =~ param_datatype + 'char *' + # Ignore for now + #elsif Template.treated_as_string_pointer =~ param_datatype + # 'char *' + elsif Template.struct_types =~ param_datatype + "#{param_datatype}" + elsif Template.struct_types_pointer =~ param_datatype + "#{param_datatype.gsub(/ *\*+$/,'')}" + else + nil # cannot be formated + end + end + + def convention_parameter(param) + "parameter_#{param}" + end + + def convention_return_variable(func_name) + "return_of_#{func_name}" + end + + def initialize_variables(params, structs, func_name=nil) + + result = '' + return result if params.first == 'void' + params.each do |param| + rpart = param.rpartition(' ') + format = Template::C.format_type(rpart.first) + if format + result += format + " #{'*' if Template.struct_types =~ rpart.first.gsub(/ *\*+$/,'')}#{Template::C.convention_parameter(rpart.last)};\n" + elsif !func_name.nil? + puts "// \"#{rpart.first}\" is not a parameter datatype that can be currently autobound. From function: \"#{func_name}\" and param: #{rpart.last}\n\n" + #raise + end + end + result + "\n" + end + + def initialize_return_var(func_datatype, func_name) + return '' if func_datatype == 'void' + result = '' + depointer_datatype = func_datatype.delete_suffix(' *') + if Template.struct_types =~ depointer_datatype + result += Template.get_module('Test') + result += Template.get_class(depointer_datatype, 'Test') + result += "#{depointer_datatype} *#{Template::C.convention_return_variable(func_name)} = (#{depointer_datatype} *)mrb_malloc(mrb, sizeof(#{depointer_datatype}));\n" + else + result += "#{func_datatype} #{Template::C.convention_return_variable(func_name)};\n" + end + result + end + + def get_kwargs(params) + init_array_body = '' + params.each do |param| + rpart = param.rpartition(' ') + init_array_body += "mrb_intern_lit(mrb, \"#{rpart.last}\"),\n" + end + init_array_body.delete_suffix!(",\n") + %{uint32_t kw_num = #{params.length}; +const mrb_sym kw_names[] = { +#{init_array_body} +}; +mrb_value kw_values[kw_num]; +const mrb_kwargs kwargs = { kw_num, 0, kw_names, kw_values, NULL }; +mrb_get_args(mrb, "|:", &kwargs); + } + end + + def parse_kwargs(params) + result = '' + skipped = 0 + params.each_with_index do |param, index| + rpart = param.rpartition(' ') + if Template.struct_types =~ rpart.first + + unwrap = Template.unwrap_struct(Template::C.convention_parameter(rpart.last), "kw_values[#{index - skipped}]", "mrb_#{rpart.first}_struct", rpart.first) + + result += Template::C.unwrap_kwarg(index - skipped,unwrap) + elsif Template.non_struct_types =~ rpart.first + unwrap = "#{Template::C.convention_parameter(rpart.last)} = #{Template.to_c(rpart.first, "kw_values[#{index - skipped}]")};" + result += Template::C.unwrap_kwarg(index - skipped,unwrap) + else + skipped += 1 + next + end + end + result + end + + def unwrap_kwarg(kwarg_iter, body_if_defined, body_if_undefined = nil, no_argument_error_message = 'Missing Keyword Argument') + %{ +if (mrb_undef_p(kw_values[#{kwarg_iter}])) { +#{body_if_undefined || "mrb_load_string(mrb, \"raise ArgumentError.new \\\"#{no_argument_error_message}\\\"\");"} +} else { +#{body_if_defined} } - } + } + end + end + end + + # methods that convert something from c-land to ruby-land + module MRuby + class << self + # convert a C function name to be + # formatted like a Ruby method name + def rubify_func_name(function) + func = function.underscore + if func.start_with? 'is_' + func = func.delete_prefix('is_') + '?' + elsif func.start_with? 'set_' + func = func.delete_prefix('set_') + '=' + else + func.delete_prefix('get_') + end + func + end + + def to_c_function_name(function_name:) + rubify_func_name(function_name) + end + + def to_getter_name(struct_name:, variable_name: nil) + rubify_func_name(variable_name) + end + def to_setter_name(struct_name:, variable_name: nil) + rubify_func_name(variable_name) + '=' + end + + def to_initializer_name(struct_name: nil) + "initialize" + end + end + end + + + class << self + + # could be unsigned attr_writer :treated_as_int def treated_as_int - @treated_as_int ||= ['int', 'unsigned int', 'long', 'short'] + @treated_as_int ||= /^((un)?signed )?int$|^((un)?signed )?long$|^((un)?signed )?short$|^((un)?signed )char$/ + end + attr_writer :treated_as_int_pointer + def treated_as_int_pointer + @treated_as_int_pointer ||= /^((un)?signed )?int \*$|^((un)?signed )?long \*$|^((un)?signed )?short \*$|^((un)?signed )char \*$/ end attr_writer :treated_as_bool def treated_as_bool - @treated_as_bool ||= ['bool'] + @treated_as_bool ||= /^bool$/ + end + attr_writer :treated_as_bool_pointer + def treated_as_bool_pointer + @treated_as_bool_pointer ||= /^bool \*$/ end attr_writer :treated_as_float def treated_as_float - @treated_as_float ||= ['float', 'double'] + @treated_as_float ||= /^float$|^double$/ + end + attr_writer :treated_as_float_pointer + def treated_as_float_pointer + @treated_as_float_pointer ||= /^float \*$|^double \*$/ end attr_writer :treated_as_string def treated_as_string - @treated_as_string ||= ['char *', 'const char *'] + @treated_as_string ||= /^(const )?char \*$/ end + # Ignore for now + #attr_writer :treated_as_string_pointer + #def treated_as_string_pointer + # @treated_as_string_pointer ||= /^(const )?char \*\*$/ + #end attr_writer :treated_as_void def treated_as_void - @treated_as_void ||= ['void'] + @treated_as_void ||= /^void$/ end def non_struct_types - treated_as_int | treated_as_bool | treated_as_float | treated_as_string | treated_as_void + @non_struct_types ||= Regexp.union(treated_as_int, treated_as_bool, treated_as_float, treated_as_string, treated_as_void) + end + def non_struct_types_pointer + @non_struct_types_pointer ||= Regexp.union(treated_as_int_pointer, treated_as_bool_pointer, treated_as_float_pointer)#, treated_as_string_pointer) + end + + attr_writer :struct_types + def struct_types + if @struct_types + @struct_types + else + raise "Struct types were not parsed\nRun 'parse_struct_types' first" + end + end + attr_writer :struct_types_pointer + def struct_types_pointer + if @struct_types_pointer + @struct_types_pointer + else + raise "Struct types were not parsed\nRun 'parse_struct_types' first" + end + end + + def parse_struct_types(structs) + struct_types = structs.keys + struct_types_pointer = struct_types.map do |string| + "^#{string} \\*$" + end + struct_types.map! do |string| + "^#{string}$" + end + @struct_types_pointer = /#{struct_types_pointer.join('|')}/ + @struct_types = /#{struct_types.join('|')}/ + end + + def valid_types + @valid_types ||= Regexp.union(non_struct_types, struct_types) + end + def valid_types_pointer + @valid_types_pointer ||= Regexp.union(non_struct_types_pointer, struct_types_pointer) + end + def all_valid_types + @all_valid_types ||= Regexp.union(valid_types, valid_types_pointer) + end + + def base(gem_name, init_body, final_body) + %{ + void + mrb_mruby_#{gem_name}_gem_init(mrb_state* mrb) { + #{init_body} +} + +void +mrb_mruby_#{gem_name}_gem_final(mrb_state* mrb) { +#{final_body} +} + } + end + + def format_method_call(func_datatype, func_name, params, is_struct=false) + result = '' + #if params.first == 'void' + # result += "return #{'*' if is_struct}#{Template::C.convention_return_variable(func_name)} = " + #end + result += "#{func_name}(" + puts '//| --- |' + puts "//#{func_name}" + unless params.first == 'void' + params.each do |param| + rpart = param.rpartition(' ') + puts '//--' + puts "//#{rpart.first}" + puts "//#{Template.struct_types =~ rpart.first}" + result += "#{'*' if Template.struct_types =~ rpart.first}#{"(#{rpart.first})&" if Template.non_struct_types_pointer =~ rpart.first}#{Template::C.convention_parameter(rpart.last)}, " + end + end + result.delete_suffix(', ') + ")" + end + + def format_set_method_call(func_datatype, func_name, params, is_struct=false) + result = format_method_call(func_datatype, func_name, params, is_struct) + ";\n" + unless func_datatype == 'void' + if Template.struct_types_pointer =~ func_datatype + result = '*' + result + end + result = "#{Template::C.convention_return_variable(func_name)} = #{result}" + if is_struct + result = '*' + result + end + end + result + end + + def format_return(func_datatype, func_name) + "return #{Template.to_mrb(func_datatype, Template::C.convention_return_variable(func_name))};" end def init_module(module_name) @@ -64,11 +335,11 @@ mrb_mruby_#{gem_name}_gem_final(mrb_state* mrb) { # define under needs the C name, not the ruby name which may be confusing def init_class(class_name, define_under, is_struct_wrapper = true) %{ -struct RClass *#{class_name.downcase}_class = mrb_define_class_under(mrb, #{define_under}, \"#{class_name}\", mrb->object_class);#{ + struct RClass *#{class_name.downcase}_class = mrb_define_class_under(mrb, #{define_under}, \"#{class_name}\", mrb->object_class);#{ if is_struct_wrapper "\nMRB_SET_INSTANCE_TT(#{class_name.downcase}_class, MRB_TT_DATA);" end - } +} } end @@ -86,56 +357,35 @@ mrb_#{function_name}(mrb_state* mrb, mrb_value self) { } end - def get_kwargs(kwarg_num, init_var_body, init_array_body) - %{ -#{init_var_body} - -uint32_t kw_num = #{kwarg_num}; -const mrb_sym kw_names[] = { -#{init_array_body} -}; -mrb_value kw_values[kw_num]; -const mrb_kwargs kwargs = { kw_num, 0, kw_names, kw_values, NULL }; -mrb_get_args(mrb, "|:", &kwargs); - } - end - - def unwrap_kwarg(kwarg_iter, body_if_defined, body_if_undefined = nil, no_argument_error_message = 'Missing Keyword Argument') - %{ -if (mrb_undef_p(kw_values[#{kwarg_iter}])) { -#{body_if_undefined || "mrb_load_string(mrb, \"raise ArgumentError.new \\\"#{no_argument_error_message}\\\"\");"} -} else { -#{body_if_defined} -} - } - end - def get_args(req_arg_hash, opt_arg_hash=nil) - raise if opt_arg_hash + raise 'opt_arg_hash feature not implemented yet' if opt_arg_hash result = '' tail = '' flags = '' req_arg_hash.each do |var_name, var_datatype| - if var_datatype != 'unsigned char' - result += "#{var_datatype} #{var_name};\n" - else - result += "mrb_int #{var_name};\n" + #if var_datatype != 'unsigned char' + if Template.non_struct_types =~ var_datatype + result += "#{Template::C.format_type(var_datatype)} #{Template::C.convention_parameter(var_name)};\n" + #else + # result += "mrb_int #{var_name};\n" end - tail += ", &#{var_name}" + tail += ", &#{Template::C.convention_parameter(var_name)}" flags += datatype_to_arg_flag(var_datatype) end result += "mrb_get_args(mrb, \"#{flags}\"#{tail});\n" end def datatype_to_arg_flag(datatype) - if treated_as_int.include? datatype + if Template.treated_as_int =~ datatype 'i' - elsif treated_as_bool.include? datatype + elsif Template.treated_as_bool =~ datatype 'b' - elsif treated_as_float.include? datatype + elsif Template.treated_as_float =~ datatype 'f' - elsif treated_as_string.include? datatype + elsif Template.treated_as_string =~ datatype 'z' + elsif Template.struct_types =~ datatype + 'o' end end @@ -145,9 +395,9 @@ if (mrb_undef_p(kw_values[#{kwarg_iter}])) { def wrap_struct(var_name, target, mrb_type, type) %{ - #{var_name} = (#{type} *)DATA_PTR(#{target}); - if(#{var_name}) #{'{'} mrb_free(mrb, #{var_name}); #{'}'} -mrb_data_init(#{target}, NULL, &#{mrb_type}); + #{var_name} = (#{type} *)DATA_PTR(#{target}); + if(#{var_name}) #{'{'} mrb_free(mrb, #{var_name}); #{'}'} + mrb_data_init(#{target}, NULL, &#{mrb_type}); #{var_name} = (#{type} *)mrb_malloc(mrb, sizeof(#{type})); } end @@ -159,39 +409,40 @@ mrb_data_init(#{target}, NULL, &#{mrb_type}); # for converting mrb to C def to_c(type, variable) - if treated_as_int.include?(type) || treated_as_bool.include?(type) + if (Template.treated_as_int =~ type) || (Template.treated_as_bool =~ type) "mrb_as_int(mrb, #{variable})" - elsif treated_as_float.include? type + elsif Template.treated_as_float =~ type "mrb_as_float(mrb, #{variable})" - elsif treated_as_string.include? type + elsif Template.treated_as_string =~ type "mrb_str_to_cstr(mrb, #{variable})" end end # for converting C to mrb def to_mrb(type, variable) - if treated_as_int.include? type + if Template.treated_as_int =~ type "mrb_fixnum_value(#{variable})" - elsif treated_as_float.include? type + elsif Template.treated_as_float =~ type "mrb_float_value(mrb, #{variable})" - elsif treated_as_bool.include? type + elsif Template.treated_as_bool =~ type "mrb_bool_value(#{variable})" - elsif treated_as_string.include? type + elsif Template.treated_as_int_pointer =~ type + "mrb_fixnum_value(*#{variable})" + elsif Template.treated_as_float_pointer =~ type + "mrb_float_value(mrb, *#{variable})" + elsif Template.treated_as_bool_pointer =~ type + "mrb_bool_value(*#{variable})" + elsif Template.treated_as_string =~ type "mrb_str_new_cstr(mrb, #{variable})" - elsif treated_as_void.include? type + elsif Template.treated_as_void =~ type 'mrb_nil_value()' + elsif Template.struct_types =~ type + "mrb_obj_value(Data_Wrap_Struct(mrb, #{type.downcase}_mrb_class, &mrb_#{type}_struct, #{variable}))" + elsif Template.struct_types_pointer =~ type + "mrb_obj_value(Data_Wrap_Struct(mrb, #{type.delete_suffix(' *').downcase}_mrb_class, &mrb_#{type.delete_suffix(' *')}_struct, #{variable}))" end end - # convert a C function name to be - # formatted like a Ruby method name - def rubify_func_name(function) - func = function.underscore - if func.start_with? 'is_' - func = func.delete_prefix('is_') + '?' - end - func.delete_prefix('get_') - end # generate a return of a ruby bound C function def return_format(function, params) @@ -242,31 +493,31 @@ mrb_data_init(#{target}, NULL, &#{mrb_type}); # wrapping an existing struct to be used by ruby def init_struct_wrapper(struct, free_body = nil) %{ - #{"void mrb_helper_#{struct}_free(mrb_state*, void*);" if free_body} + #{"void mrb_helper_#{struct}_free(mrb_state*, void*);" if free_body} -static const struct mrb_data_type mrb_#{struct}_struct = { -"#{struct}", -#{ + static const struct mrb_data_type mrb_#{struct}_struct = { + "#{struct}", + #{ if free_body "mrb_helper_#{struct}_free" else "mrb_free" end } - }; - #{ + }; + #{ if free_body %{ void mrb_helper_#{struct}_free(mrb_state* mrb, void*ptr) { #{struct} *struct_data = (#{struct}*)ptr; -#{free_body} + #{free_body} mrb_free(mrb, ptr); } } end - } + } } end |
