summaryrefslogtreecommitdiffhomepage
path: root/src/templates.rb
diff options
context:
space:
mode:
authorrealtradam <[email protected]>2022-08-31 08:29:23 -0400
committerrealtradam <[email protected]>2022-08-31 08:29:23 -0400
commitc51ded6c4efef4e0ef5a157a72ae79c402b7964b (patch)
tree46802e9bfaae9d97538ab94285c3357f7ca65c94 /src/templates.rb
parent8caf4473882f3eab9018b0ed4500792d459bdc98 (diff)
downloadFelBind-c51ded6c4efef4e0ef5a157a72ae79c402b7964b.tar.gz
FelBind-c51ded6c4efef4e0ef5a157a72ae79c402b7964b.zip
starting work on the backend system
Diffstat (limited to 'src/templates.rb')
-rw-r--r--src/templates.rb623
1 files changed, 623 insertions, 0 deletions
diff --git a/src/templates.rb b/src/templates.rb
new file mode 100644
index 0000000..7d88876
--- /dev/null
+++ b/src/templates.rb
@@ -0,0 +1,623 @@
+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
+
+ 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 format_mrb_type(param_datatype)
+ if Template.treated_as_int =~ param_datatype
+ 'mrb_int'
+ elsif Template.treated_as_int_pointer =~ param_datatype
+ 'mrb_int'
+ elsif Template.treated_as_bool =~ param_datatype
+ 'mrb_bool'
+ elsif Template.treated_as_bool_pointer =~ param_datatype
+ 'mrb_bool'
+ elsif Template.treated_as_float =~ param_datatype
+ 'mrb_float'
+ elsif Template.treated_as_float_pointer =~ param_datatype
+ 'mrb_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
+ "mrb_value" #"#{param_datatype}"
+ elsif Template.struct_types_pointer =~ param_datatype
+ "mrb_value" #"#{param_datatype.gsub(/ *\*+$/,'')}"
+ else
+ nil # cannot be formated
+ end
+ end
+
+ def convention_parameter(param)
+ "parameter_#{param}"
+ end
+
+ def convention_mrb_parameter(param)
+ "parameter_mrb_#{param}"
+ end
+
+ def convention_return_variable(func_name)
+ "return_of_#{func_name}"
+ end
+
+ def initialize_variables_for_kwargs(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 needs_mrb_conversion?(datatype)
+ # test = Template::C.datatype_to_arg_flag(datatype)
+ # if ['i','b','z','f'].include? test
+ # false
+ # elsif ['o'].include? test
+ # true
+ # else
+ # nil
+ # end
+ #end
+
+ def initialize_variables_for_args(params, structs, func_name=nil)
+ result = ''
+ return result if params.first == 'void'
+ params.each do |param|
+ rpart = param.rpartition(' ')
+ format = Template::C.format_mrb_type(rpart.first)
+ if format
+ if Template.struct_types =~ rpart.first || Template.struct_types_pointer =~ rpart.first
+ result += format + " #{Template::C.convention_mrb_parameter(rpart.last)};\n"
+ else
+ result += format + " #{Template::C.convention_parameter(rpart.last)};\n"
+ end
+ 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.underscore}\"),\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(' ')
+ datatype, _space, var_name = param.rpartition(' ')
+ if Template.struct_types_all =~ datatype
+
+ unwrap = Template.unwrap_struct(Template::C.convention_parameter(var_name), "kw_values[#{index - skipped}]", "mrb_#{datatype.delete_suffix(' *')}_struct", datatype.delete_suffix(' *'))
+
+ result += Template::C.unwrap_kwarg(index - skipped,unwrap, nil, "Missing kwarg: #{var_name.underscore}")
+ elsif Template.non_struct_types_all =~ datatype
+ unwrap = "#{Template::C.convention_parameter(var_name)} = #{Template.to_c(datatype, "kw_values[#{index - skipped}]")};"
+ result += Template::C.unwrap_kwarg(index - skipped, unwrap, nil, "#{var_name.underscore}")
+ else
+ skipped += 1
+ next
+ end
+ end
+ result
+ end
+
+ # unwrap structs
+ # convert floats?
+ # ignore string
+ # ignore boolean
+ # ignore int
+ def parse_args(params)
+ result = ''
+ params.each do |param|
+ datatype, _space, var_name = param.rpartition(' ')
+ next unless Template.struct_types =~ datatype || Template.struct_types_pointer =~ datatype
+ format = Template::C.format_type(datatype)
+ if format
+ # init var
+ result += format + " *#{Template::C.convention_parameter(var_name)};\n"
+ # unwrap var
+ result += Template.unwrap_struct(Template::C.convention_parameter(var_name), Template::C.convention_mrb_parameter(var_name), "mrb_#{datatype.delete_suffix(' *')}_struct", datatype.delete_suffix(' *'))
+ else
+ # error
+ 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, params=[])
+ func = function.underscore
+ if func.start_with? 'is_'
+ func = func.delete_prefix('is_') + '?'
+ elsif func.start_with? 'set_'
+ func = func.delete_prefix('set_') + '=' if params.count == 1 && params.first != 'void'
+ end
+ func.delete_prefix('get_')
+ 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 ||= /^((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$/
+ 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$/
+ 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 ||= /^(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$/
+ end
+
+ def non_struct_types
+ @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
+ def non_struct_types_all
+ @non_struct_types_all ||= Regexp.union(non_struct_types, non_struct_types_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
+ attr_writer :struct_types_all
+ def struct_types_all
+ @struct_types_all ||= Regexp.union(struct_types, struct_types_pointer)
+ 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}("
+ unless params.first == 'void'
+ params.each do |param|
+ rpart = param.rpartition(' ')
+ 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)
+ "struct RClass *#{module_name.downcase}_module = mrb_define_module(mrb, \"#{module_name}\");"
+ end
+
+ def get_module(module_name)
+ "struct RClass *#{module_name.downcase}_mrb_module = mrb_module_get(mrb, \"#{module_name}\");\n"
+ end
+
+ def get_class(class_name, defined_under)
+ "struct RClass *#{class_name.downcase}_mrb_class = mrb_class_get_under(mrb, #{defined_under.downcase}_mrb_module, mrb_#{class_name}_struct.struct_name);\n"
+ end
+
+ def init_module_function(module_name, function_name, mrb_function_name, mrb_args)
+ %{
+ mrb_define_module_function(mrb, #{module_name}, "#{function_name}", mrb_#{mrb_function_name}, #{mrb_args});
+ }
+ end
+
+ # 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);#{
+ if is_struct_wrapper
+ "\nMRB_SET_INSTANCE_TT(#{class_name.downcase}_class, MRB_TT_DATA);"
+ end
+}
+ }
+ end
+
+ def function(function_name, body)
+ %{
+static mrb_value
+mrb_#{function_name}(mrb_state* mrb, mrb_value self) {
+#{body}
+}
+ }
+ end
+
+ def init_function(class_name, function_name, mrb_function_name, mrb_args)
+ %{mrb_define_method(mrb, #{class_name}, "#{function_name}", mrb_#{mrb_function_name}, #{mrb_args});
+ }
+ end
+
+ def get_args(req_arg_hash, opt_arg_hash=nil)
+ 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'
+ #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
+ if Template.struct_types_all =~ var_datatype
+ tail += ", &#{Template::C.convention_mrb_parameter(var_name)}"
+ else
+ tail += ", &#{Template::C.convention_parameter(var_name)}"
+ end
+ flags += datatype_to_arg_flag(var_datatype)
+ end
+ result += "mrb_get_args(mrb, \"#{flags}\"#{tail});\n"
+ end
+
+ def datatype_to_arg_flag(datatype)
+ if Template.treated_as_int =~ datatype || Template.treated_as_int_pointer =~ datatype
+ 'i'
+ elsif Template.treated_as_bool =~ datatype || Template.treated_as_bool_pointer =~ datatype
+ 'b'
+ elsif Template.treated_as_float =~ datatype || Template.treated_as_float_pointer =~ datatype
+ 'f'
+ elsif Template.treated_as_string =~ datatype
+ 'z'
+ elsif Template.struct_types =~ datatype || Template.struct_types_pointer =~ datatype
+ 'o'
+ else # failed to match
+ nil
+ end
+ end
+
+ def unwrap_struct(var_name, target, mrb_type, type)
+ %{#{var_name} = DATA_GET_PTR(mrb, #{target}, &#{mrb_type}, #{type});\n}
+ end
+
+ 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} *)mrb_malloc(mrb, sizeof(#{type}));
+ }
+ end
+
+ def define_module(module_name)
+ %{struct RClass *#{module_name.downcase} = mrb_define_module(mrb, "#{module_name}");
+ }
+ end
+
+ # for converting mrb to C
+ def to_c(type, variable)
+ if (Template.treated_as_int =~ type) || (Template.treated_as_bool =~ type)
+ "mrb_as_int(mrb, #{variable})"
+ elsif (Template.treated_as_int_pointer =~ type) || (Template.treated_as_bool_pointer =~ type)
+ "mrb_as_int(mrb, #{variable})"
+ elsif Template.treated_as_float =~ type
+ "mrb_as_float(mrb, #{variable})"
+ elsif Template.treated_as_float_pointer =~ type
+ "mrb_as_float(mrb, #{variable})"
+ elsif Template.treated_as_string =~ type
+ "mrb_str_to_cstr(mrb, #{variable})"
+ else
+ "#{type} and #{variable}"
+ end
+ end
+
+ # for converting C to mrb
+ def to_mrb(type, variable)
+ if Template.treated_as_int =~ type
+ "mrb_fixnum_value(#{variable})"
+ elsif Template.treated_as_float =~ type
+ "mrb_float_value(mrb, #{variable})"
+ elsif Template.treated_as_bool =~ type
+ "mrb_bool_value(#{variable})"
+ 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 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
+
+
+ # generate a return of a ruby bound C function
+ def return_format(function, params)
+ func_rpart = function.rpartition(' ')
+ func_datatype = func_rpart.first
+ func_name = func_rpart.last
+ result = ''
+ if func_datatype == 'void'
+ if params.first == 'void'
+ result = "#{func_name}();\nreturn mrb_nil_value();"
+ else
+ result = "#{func_name}("
+ result += params.first.rpartition(' ').last
+
+ params.drop(1).each do |param|
+ result += ", #{param.rpartition(' ').last}"
+ end
+ result += ");\nreturn mrb_nil_value();"
+ end
+ elsif params.first == 'void'
+ result = "return " + Tplt.to_mrb(func_datatype, "#{func_name}()") + ';'
+ else
+ temp_params = params.first.rpartition(' ').last
+
+ params.drop(1).each do |param|
+ temp_params += ", #{param.rpartition(' ').last}"
+ end
+ result = 'return ' + Tplt.to_mrb(func_datatype, "#{func_name}(#{temp_params})") + ';'
+ end
+ result
+ end
+
+ # doesnt seem correct?
+ def return_format_struct(function)
+ func_rpart = function.rpartition(' ')
+ func_datatype = func_rpart.first.delete_suffix(' *')
+ func_name = func_rpart.last
+ "return mrb_obj_value(Data_Wrap_Struct(mrb, #{func_datatype.downcase}_mrb_class, &mrb_#{func_datatype}_struct, return_value));"
+ end
+
+ def make_mrb_obj_from_struct(mrb_var, func, struct_var)
+ func_rpart = func.rpartition(' ')
+ func_datatype = func_rpart.first.delete_suffix(' *')
+ func_name = func_rpart.last
+ "mrb_data_init(#{mrb_var}, #{struct_var}, &mrb_#{func_rpart.first}_struct);\n"
+ end
+
+ # 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}
+
+ 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}
+mrb_free(mrb, ptr);
+ }
+ }
+ end
+ }
+ }
+ end
+
+ end
+end
+