summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
author_Tradam <[email protected]>2022-03-30 21:35:12 -0400
committerGitHub <[email protected]>2022-03-30 21:35:12 -0400
commit2f93334dc43f92f50196f1c39c7fb9ce40f4c562 (patch)
tree209f02db6fed6499c404b4f477753787560fd2b9
parentf3d972c829a6f1e0bc00906d9e4220ef37db3ba2 (diff)
downloadFelBind-2f93334dc43f92f50196f1c39c7fb9ce40f4c562.tar.gz
FelBind-2f93334dc43f92f50196f1c39c7fb9ce40f4c562.zip
Refactor
-rw-r--r--Readme.mdown62
-rw-r--r--generate.rb189
-rw-r--r--rework.mdown62
-rw-r--r--templates.rb415
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