diff options
| author | realtradam <[email protected]> | 2022-08-31 08:29:23 -0400 |
|---|---|---|
| committer | realtradam <[email protected]> | 2022-08-31 08:29:23 -0400 |
| commit | c51ded6c4efef4e0ef5a157a72ae79c402b7964b (patch) | |
| tree | 46802e9bfaae9d97538ab94285c3357f7ca65c94 /src/templates.rb | |
| parent | 8caf4473882f3eab9018b0ed4500792d459bdc98 (diff) | |
| download | FelBind-c51ded6c4efef4e0ef5a157a72ae79c402b7964b.tar.gz FelBind-c51ded6c4efef4e0ef5a157a72ae79c402b7964b.zip | |
starting work on the backend system
Diffstat (limited to 'src/templates.rb')
| -rw-r--r-- | src/templates.rb | 623 |
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 + |
