diff options
| author | realtradam <[email protected]> | 2023-05-25 15:02:05 -0400 |
|---|---|---|
| committer | realtradam <[email protected]> | 2023-05-25 15:02:05 -0400 |
| commit | 87727cdfbcb3d93f2a3216ab8dc20f40d5cfe985 (patch) | |
| tree | 86ead27df419c9e8a9aa35177f2dfd2ce1a43bdc | |
| parent | 9f43631fa25346db798ebe3c3358ff4ae4377601 (diff) | |
| download | FelBind-87727cdfbcb3d93f2a3216ab8dc20f40d5cfe985.tar.gz FelBind-87727cdfbcb3d93f2a3216ab8dc20f40d5cfe985.zip | |
basic params
| -rw-r--r-- | .gitignore | 11 | ||||
| -rw-r--r-- | .rubocop.yml | 13 | ||||
| -rw-r--r-- | .tool-versions | 1 | ||||
| -rw-r--r-- | CHANGELOG.md | 5 | ||||
| -rw-r--r-- | FelBind.gemspec | 39 | ||||
| -rw-r--r-- | Gemfile | 7 | ||||
| -rw-r--r-- | Gemfile.lock | 35 | ||||
| -rw-r--r-- | LICENSE.txt | 21 | ||||
| -rw-r--r-- | README.md | 33 | ||||
| -rw-r--r-- | Rakefile | 27 | ||||
| -rwxr-xr-x | bin/console | 15 | ||||
| -rwxr-xr-x | bin/setup | 8 | ||||
| -rwxr-xr-x | exe/felbind | 78 | ||||
| -rw-r--r-- | ideas/stuff.rb | 80 | ||||
| -rw-r--r-- | lib/FelBind.rb | 156 | ||||
| -rw-r--r-- | lib/FelBind/backend/test.rb (renamed from src/backend/test.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/backend/uctags.rb (renamed from src/backend/uctags.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/frontend/mruby.rb | 502 | ||||
| -rw-r--r-- | lib/FelBind/generate.rb (renamed from src/generate.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/sample.config.rb (renamed from src/sample.config.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/scan.rb (renamed from src/scan.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/templates.rb (renamed from src/templates.rb) | 0 | ||||
| -rw-r--r-- | lib/FelBind/version.rb | 5 | ||||
| -rw-r--r-- | planning/structure.png | bin | 0 -> 212489 bytes | |||
| -rw-r--r-- | sig/FelBind.rbs | 4 | ||||
| -rw-r--r-- | src/backend/test.h | 12 |
26 files changed, 1011 insertions, 41 deletions
@@ -1,3 +1,13 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +/*.json json.json raylib.h chipmunk.h @@ -9,4 +19,3 @@ temp temp.c build build/* -planning/*.png diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..e3462a7 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,13 @@ +AllCops: + TargetRubyVersion: 2.6 + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + Enabled: true + EnforcedStyle: double_quotes + +Layout/LineLength: + Max: 120 diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 0b2d858..0000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -ruby 3.1.2 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d3fd5d9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## [Unreleased] + +## [0.1.0] - 2022-09-15 + +- Initial release diff --git a/FelBind.gemspec b/FelBind.gemspec new file mode 100644 index 0000000..83dbe12 --- /dev/null +++ b/FelBind.gemspec @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative "lib/FelBind/version" + +Gem::Specification.new do |spec| + spec.name = "FelBind" + spec.version = FelBind::VERSION + spec.authors = ["realtradam"] + spec.email = ["[email protected]"] + + spec.summary = "Write a short summary, because RubyGems requires one." #TODO + #spec.description = "TODO: Write a longer description or delete this line." + spec.homepage = "https://github.com/realtradam/FelBind"#"TODO: Put your gem's website or public repo URL here." + spec.license = "MIT" + spec.required_ruby_version = ">= 2.6.0" + + spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'" + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage#"TODO: Put your gem's public repo URL here." + #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").reject do |f| + (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + # Uncomment to register a new dependency of your gem + # spec.add_dependency "example-gem", "~> 1.0" + + # For more information and examples about making a new gem, check out our + # guide at: https://bundler.io/guides/creating_gem.html +end @@ -2,6 +2,9 @@ source "https://rubygems.org" -# gem "rails" +# Specify your gem's dependencies in FelBind.gemspec +gemspec -gem "cast", "~> 0.3.1" +gem "rake", "~> 13.0" + +gem "rubocop", "~> 1.21" diff --git a/Gemfile.lock b/Gemfile.lock index c32dc74..602c7ea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,42 @@ +PATH + remote: . + specs: + FelBind (0.1.0) + GEM remote: https://rubygems.org/ specs: - cast (0.3.1) + ast (2.4.2) + json (2.6.3) + parallel (1.22.1) + parser (3.2.1.0) + ast (~> 2.4.1) + rainbow (3.1.1) + rake (13.0.6) + regexp_parser (2.7.0) + rexml (3.2.5) + rubocop (1.45.1) + json (~> 2.3) + parallel (~> 1.10) + parser (>= 3.2.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.24.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.26.0) + parser (>= 3.2.1.0) + ruby-progressbar (1.11.0) + unicode-display_width (2.4.2) PLATFORMS x86_64-linux DEPENDENCIES - cast (~> 0.3.1) + FelBind! + rake (~> 13.0) + rubocop (~> 1.21) BUNDLED WITH - 2.3.20 + 2.4.6 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..40cd76e --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 realtradam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8210486 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# FelBind + +Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/FelBind`. To experiment with that code, run `bin/console` for an interactive prompt. + +TODO: Delete this and the text above, and describe your gem + +## Installation + +Install the gem and add to the application's Gemfile by executing: + + $ bundle add FelBind + +If bundler is not being used to manage dependencies, install the gem by executing: + + $ gem install FelBind + +## Usage + +TODO: Write usage instructions here + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/FelBind. + +## License + +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). @@ -1,25 +1,8 @@ -require_relative 'scan.rb' -require_relative 'generate.rb' +# frozen_string_literal: true -desc 'create parsed file with ctags' -task :scan do - # for each file in target directory - # parse file - # output to build/parse - Dir.mkdir('build') unless File.exists?('build') - Dir.each_child('target') do |file| - Scan.scan("target/#{file}", 'build/parsed.json') - end -end +require "bundler/gem_tasks" +require "rubocop/rake_task" -desc 'build bindings from the parsed file' -task :generate do - # read parse file - # output to build/bind - Generate.generate('build/parsed.json', '../FelFlameEngine/mrbgems/mruby-raylib/src/bind.c') -end +RuboCop::RakeTask.new -task :make_gem do - # read bind file - # output to build/gem -end +task default: :rubocop diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..871d911 --- /dev/null +++ b/bin/console @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "FelBind" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/exe/felbind b/exe/felbind new file mode 100755 index 0000000..efb432a --- /dev/null +++ b/exe/felbind @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +require 'fileutils' +require 'FelBind' + +def print_help + commands = [ + " # Prints Help", + " help", + "", + " # prints out intermediate", + " parse -<backend> <input-file list>", + " Valid Backends:", + " -uctags (default)", + "", + " # Creates bindings gem", + " build -<frontend> <input-file> <destination>", + " Valid Frontends:", + " -mruby (default)", + ] + + commands.each { |cmd| puts cmd } +end + +if (ARGV.size == 0) || (ARGV[0] == 'help') + print_help + return +end + +if ARGV[0] == 'parse' + file_path = '' + if ARGV[1].start_with? '-' + if ARGV[1] != '-uctags' + puts 'invalid backend' + return + else + file_path = ARGV[2] + end + else + file_path = ARGV[1] + end + + if !File.exist?(file_path) + puts "invalid file path" + return + end + + pp FelBind::Backends::UCTags.parse(file_path) +elsif ARGV[0] == 'build' + file_path = '' + dest_dir = '' + if ARGV[1] == '-mruby' + file_path = ARGV[2] + dest_dir = ARGV[3] + else + file_path = ARGV[1] + dest_dir = ARGV[2] + end + + if !File.exist?(file_path) + puts "invalid file path" + return + end + + puts "TEST" + FileUtils.mkpath(dest_dir) unless File.exist?(dest_dir) + + #if !File.directory? + # puts "invalid destination" + # return + #end + + FelBind::Frontend::Mruby.build(file_path, dest_dir) +else + print_help + return +end + diff --git a/ideas/stuff.rb b/ideas/stuff.rb new file mode 100644 index 0000000..276a101 --- /dev/null +++ b/ideas/stuff.rb @@ -0,0 +1,80 @@ +# frozen_string_literal:true + +# --- + +mgem = FelBind::BindGem.new(gem_name: "basic_example") + +mgem.add_class("BasicExample") + +mgem.add_function(class_name: "BasicExample", function_name: "say_hello") do |func| + func.content = "printf(\"Hello World\n\");" + func.return_call do |rc| + rc.type = "nil" + end +end + +puts mgem.build + +# --- + +mgem = FelBind::BindGem.new + +mgem.add_class("ArgumentsAndReturnExample") + +mgem.add_function(class: "ArgumentsAndReturnExample", name: "multiply_numbers") do |func| + func.get_args do |args| + args.int "first_input" + args.int "second_input" + end + + func.return_call do |rc| + rc.type = "int" + rc.val = "#{func.arg("first_input")} * #{func.arg("second_input")}" + end +end + +# --- + +mgem = BindGem.new + +mgem.add_class("KeywordArgumentsExample") + +mgem.add_function(class: "KeywordArgumentsExample", name: "multiply_numbers") do |func| + func.get_kwargs do |kwargs| + kwargs.args x: "int", y: "int" + end + + func.return_call do |rc| + rc.rtype = "int" + rc.val = "#{func.kwarg("x")} * #{func.kwarg("y")}" + end +end + +# --- + +mgem = BindGem.new + +mgem.add_class("Color") + +mgem.add_struct(class: "Color") do |struct| + struct.initializer = true + struct.member( + name: "r", + ctype: "char", + rtype: "int", + accessor: true + ) + struct.member( + name: "g", + ctype: "char", + rtype: "int", + accessor: true + ) + struct.member( + name: "b", + ctype: "char", + rtype: "int", + accessor: true + ) +end + diff --git a/lib/FelBind.rb b/lib/FelBind.rb new file mode 100644 index 0000000..1ca1cff --- /dev/null +++ b/lib/FelBind.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +=begin +require_relative "FelBind/version" +require_relative "FelBind/backend/uctags.rb" +require_relative "FelBind/frontend/mruby.rb" +=end + +module FelBind + class Error < StandardError; end + # Your code goes here... + + # Binding C to mruby + class BindGem + attr_accessor :gem_name + + def initialize(gem_name:) + self.gem_name = gem_name + end + + def add_class(name) + class_names.push name + end + + def add_function(class_name:, function_name:, &block) + functions.push Function.new(class_name: class_name, function_name: function_name) + block.call(functions.last) + end + + private + + def functions + @functions ||= [] + end + + def class_names + @class_names ||= [] + end + + # function + class Function + attr_accessor :content, :name, :class_name, :return_call_val, :args + + def initialize(class_name:, function_name:) + self.class_name = class_name + self.name = function_name + end + + def return_call(&block) + self.return_call_val = ReturnCall.new + block.call(return_call_val) + end + + def build_get_vars + result = "" + expect = "" + addresses = "" + args.arguments.each do |param| + if param.first == :int + result += "mrb_int #{arg(param.last)};\n" + expect += "i" + addresses += ", &#{arg(param.last)}" + end + end + addresses.delete_prefix! ", " + result += "mrb_get_args(mrb, \"#{expect}\", #{addresses});\n" + end + + def build + function = "static mrb_value\n" + function += "felbind_#{name}(mrb_state *mrb, mrb_value self){\n" + function += build_get_vars + function += "#{content}\n" + function += "#{return_call_val.build}" + function += "}\n" + function + end + + def arg(name) + "felflame_var_#{name}" + end + + def get_args(&block) + self.args = Args.new + block.call(args) + end + + # args + class Args + def arguments + @arguments ||= [] + end + + def int(name) + arguments.push [:int, name] + end + end + + # return call + class ReturnCall + attr_accessor :type, :val + + def build + if type == "nil" + "return mrb_nil_value();\n" + elsif type == "int" + "return mrb_fixnum_value(#{val});\n" + end + end + end + end + end + + # bind gem + class BindGem + def build + result = "" + result += insert_includes + functions.each do |func_obj| + result += func_obj.build + end + result += build_init + result += build_final + result + end + + private + + def insert_includes + "#include <mruby.h>\n#include <stdio.h>\n" + end + + def build_init + result = "" + result += "void mrb_#{gem_name}_gem_init(mrb_state* mrb) {\n" + class_names.each do |class_name| + result += "struct RClass *#{class_name}_class = mrb_define_module(mrb, \"#{class_name}\");\n" + end + functions.each do |func| + result += "mrb_define_class_method(mrb, #{func.class_name}_class, \"#{func.name}\", felbind_#{func.name}," + if(func.args.arguments.size.zero?) + result += " MRB_ARGS_NONE()" + else + result += " MRB_ARGS_REQ(#{func.args.arguments.size})" + end + result += ");\n" + end + result += "}\n" + result + end + + def build_final + "void mrb_#{gem_name}_gem_final(mrb_state* mrb) {}\n" + end + end +end diff --git a/src/backend/test.rb b/lib/FelBind/backend/test.rb index 956ccca..956ccca 100644 --- a/src/backend/test.rb +++ b/lib/FelBind/backend/test.rb diff --git a/src/backend/uctags.rb b/lib/FelBind/backend/uctags.rb index 010773e..010773e 100644 --- a/src/backend/uctags.rb +++ b/lib/FelBind/backend/uctags.rb diff --git a/lib/FelBind/frontend/mruby.rb b/lib/FelBind/frontend/mruby.rb new file mode 100644 index 0000000..4d2fb87 --- /dev/null +++ b/lib/FelBind/frontend/mruby.rb @@ -0,0 +1,502 @@ +#require 'optparse' +require 'json' +require 'active_record' # use to make strings to snake_case. probably overkill +#require_relative './templates.rb' +#LibraryName = 'Test' + +module FelBind + module Frontend + class Mruby < Hash + class << self + + end + + attr_accessor :intermediate + + def initialize(file_path) + # TODO wierd Struct_Deinit thing in the flowchart + # ignored it for now + self[:headers] = "" + self[:structs] = [] + # This is how each array item looks + #{ + # struct_type_init: "", + # struct_getter_method: [], + # struct_setter_method: [], + # struct_initializer: "", + #} + self[:initializer] = { + modules: { name: "", code: "" }, + classes: [] + { + name: "", + + }, + } + + self[:methods] = "" + + self.intermediate = JSON.parse(File.read(file_path)) + end + + def gen_headers + self[:headers] + end + + + # module Methods + # class << self + + # def build + # end + + # def initialize_vars + # end + + # end + # end + + # module Structs + # end + end + end +end + +class Generate + class << self +=begin + options = {} + OptionParser.new do |parser| + parser.banner = "Usage: example.rb [options]" + + parser.on("-gGLUE", "--glue=GLUE", "Path to file(defaults to ./glue.rb)") do |glue| + options[:glue] = glue + end + + parser.on('-cCONFIG', '--config=CONFIG', 'Path to config file') do |config| + options[:config] = config + end + end.parse! + options[:glue] ||= './glue.json' +=end + def generate(file, destination) + glue = JSON.parse(File.read(file)) + + # configuration + #Template.treated_as_int |= ['unsigned char'] + + $phase1 = {} + $phase2 = {} + $phase3 = {} + $phase4 = {} + $phase5 = {} + $complete_phase1 = {} + $complete_phase2 = {} + $complete_phase3 = {} + $complete_phase4 = {} + $complete_phase5 = {} + + $result = "" + $includes = %{ +#include <raylib.h> +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/data.h> +#include <mruby/class.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include <mruby/compile.h> +#include <stdlib.h> + } + + + $defines = "" + $init_body = "" + Template.parse_struct_types(glue.last) + + + # convert types + # TODO need to make this built in + # functionality(with scanner + generator) + glue.first.keys.each do |k| + rpart = k.rpartition(' ') + + #glue.first[ mappings[k] ] = glue.first.delete(k) if mappings[k] + if 'Texture2D' == rpart.first + glue.first["Texture #{rpart.last}"] = glue.first.delete(k) + elsif 'RenderTexture2D' == rpart.first + glue.first["RenderTexture #{rpart.last}"] = glue.first.delete(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 (Template.non_struct_types =~ func.rpartition(' ').first) && (params[0] == 'void') + $phase2[func] = params + else + no_struct_param = true + params.each do |param| + if !(Template.non_struct_types =~ param.rpartition(' ').first) + no_struct_param = false + break + end + end + if no_struct_param + if Template.non_struct_types =~ func.rpartition(' ').first + $phase3[func] = params + else + $phase4[func] = params + end + else + $phase5[func] = params + end + end + end + # also for display statistics + def debug_mark_binding(func, params) + if $phase1.include? func + $complete_phase1[func] = params + elsif $phase2.include? func + $complete_phase2[func] = params + elsif $phase3.include? func + $complete_phase3[func] = params + elsif $phase4.include? func + $complete_phase4[func] = params + elsif $phase5.include? func + $complete_phase5[func] = params + end + end + + $all_params = [] + $bound_params = [] + # generates structs, accessors, and initializers + glue.last.each do |struct, params| + $defines += Template.init_struct_wrapper(struct) + $init_body += Template.init_class(struct, LibraryName.downcase) + init_vars = '' + + params.each do |param| + $all_params.push param + param_datatype, _space, param_name = param.rpartition(' ') + + #next unless Template.non_struct_types =~ param_datatype + + # getter + # take no params + # unwrap struct + # return(using correct type conversion) + body = Template.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) + + # if non struct + if Template.non_struct_types_all =~ param_datatype + $bound_params.push param + if Template.non_struct_types =~ param_datatype + body += "return #{Template.to_mrb(param_datatype, "struct_#{struct.downcase}->#{param_name}")};\n" + else # pointer + body += "return #{Template.to_mrb(param_datatype, "struct_#{struct.downcase}->#{param_name}")};\n" + end + # elseif struct TODO + elsif Template.struct_types_all =~ param_datatype + if Template.struct_types =~ param_datatype + next # TODO + Template.wrap_struct(var_name, target, mrb_type, type) + else # pointer + next # TODO + end + # init var + # unwrap struct + # initialize struct as ruby object + # set to var + # end + else + next + end + $defines += Template.function("#{struct}_get_#{param_name}", body) + $init_body += Template.init_function("#{struct.downcase}_class", Template::MRuby.rubify_func_name(param_name), "#{struct}_get_#{param_name}", "MRB_ARGS_NONE()") + + # setter + # init var of correct type + # take 1 arg param + # unwrap struct + # set value in struct + # return same value + body = '' + # TODO check this and why it is kwargs? + body += Template::C.initialize_variables_for_kwargs([param], glue.last, "Struct: #{struct}") + body += Template.get_args({ "#{param_name}": "#{param_datatype}" }) + body += Template.unwrap_struct("#{struct} *struct_#{struct.downcase}", 'self', "mrb_#{struct}_struct", struct) + + if Template.non_struct_types_all =~ param_datatype + # if its a pointer + if Template.non_struct_types_pointer =~ param_datatype + body += "*struct_#{struct.downcase}->#{param_name} = #{Template::C.convention_parameter(param_name)};\n" + body += "return #{Template.to_mrb(param_datatype.delete_suffix(' *'), Template::C.convention_parameter(param_name))};\n" + else + 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" + end + elsif Template.struct_types_all =~ param_datatype + next + if Template.struct_types_pointer =~ param_datatype + #TODO + else + #TODO + end + end + + + $defines += Template.function("#{struct}_set_#{param_name}", body) + $init_body += Template.init_function("#{struct.downcase}_class", "#{Template::MRuby.rubify_func_name(param_name)}=", "#{struct}_set_#{param_name}", "MRB_ARGS_REQ(1)") + + + end + + ## initializer + # init the struct(using mrb to allocate) + # get values + # assign values to struct + # wrap struct + # return self + body = '' + 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}(" + + init_array_body = '' + unwrapped_kwargs = '' + params.each_with_index do |param, index| + temp = param + temp_rpart = temp.rpartition(' ') + #if temp_rpart.first == 'const char *' + # temp = 'char *' + temp_rpart.last + #end + #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 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", nil, "Missing kwarg: #{temp_rpart.last.underscore}") + else + # this is for structs or "undetermined" types + # doesnt work yet + next + #unwrapped_kwargs += Tplt.unwrap_kwarg(index, "wrapped_value->#{temp_rpart.last} = (#{temp_rpart.first})kw_values[#{index}];\n") + end + end + 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 += Template.function("#{struct}_initialize", body) + $init_body += Template.init_function("#{struct.downcase}_class", "initialize", "#{struct}_initialize", "MRB_ARGS_OPT(1)") + + end + + # generates functions + glue.first.each do |func, params| + # func = function name with return type + # func_datatype = function return type + # func_name = function name + # params = array of params with their data types(void means none) + rpart = func.rpartition(' ') + func_datatype = rpart.first + func_name = rpart.last + + # 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 = '' + + # use kwargs + if params.count > 1 + body += Template::C.initialize_variables_for_kwargs(params, glue.last, func_name) + + body += Template::C.get_kwargs(params) + + body += Template::C.parse_kwargs(params) + body += "\n" # formatting + # use args + elsif params.first != 'void' + body += Template::C.initialize_variables_for_args(params, glue.last, func_name) + param_rpart = params.first.rpartition(' ') + body += Template.get_args({ "#{param_rpart.last}": "#{param_rpart.first}" }) + body += Template::C.parse_args(params) + end + + 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, params), 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()") + + debug_mark_binding(func, params) + else Tplt.non_struct_types.include? func_datatype # accept params + # detecting if there is no struct param(wont need this in the future) + no_struct_param = true + params.each do |param| + if !(Tplt.non_struct_types.include? param.rpartition(' ').first) + no_struct_param = false + break + end + end + if no_struct_param + #if true# Tplt.non_struct_types.include? func.rpartition(' ').first + #$phase3[func] = params + # --- + body = '' + #body = Tplt.return_format(func, params) + init_var_body = '' + init_array_body = '' + unwrapped_kwargs = '' + params.each_with_index do |param, index| + temp = param + temp_rpart = temp.rpartition(' ') + if temp_rpart.first == 'const char *' + temp = 'char *' + temp_rpart.last + end + 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") + end + + # if return isnt regular types, add struct to init + unless Tplt.non_struct_types.include? func_datatype + #init_var_body += "#{func_datatype} *wrapped_value = {0};\n" + end + + body = Tplt.get_kwargs(params.length, init_var_body, init_array_body) + body += unwrapped_kwargs + + # if return isnt regular types, use struct return format + if Tplt.non_struct_types.include? func_datatype + body += Tplt.return_format(func, params) + else + body += Tplt.get_module(LibraryName) + body += Tplt.get_class(func_datatype, LibraryName.downcase) + body += "#{func_datatype} *wrapped_value = (#{func_datatype} *)mrb_malloc(mrb, sizeof(#{func_datatype}));\n" + body += "*wrapped_value = #{func_name}(" + params.each do |param| + temp_rpart = param.rpartition(' ') + body += "#{temp_rpart.last}, " + end + body.delete_suffix!(', ') + body += ");\n" + 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 + # --- + #puts func + debug_mark_binding(func, params) + #end + else + #$phase5[func] = params + end + end +end +raise 'end of testing' +=end + end + $init_body.prepend(Template.define_module(LibraryName)) + + $result = %{ +#{$includes} +#{$defines} +#{Template.base(LibraryName.downcase, $init_body, nil)} + } + + + #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" + + $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 += "//Struct Accessors: #{$bound_params.length} / #{$all_params.length}\n//---\n" + + $result += "\n" + + puts $result + + puts '/*' + puts "UNBOUND:" + pp $all_params - $bound_params + puts + puts "BOUND:" + pp $bound_params + puts '*/' + + File.write(destination, $result) + end + end +end diff --git a/src/generate.rb b/lib/FelBind/generate.rb index 4fa41be..4fa41be 100644 --- a/src/generate.rb +++ b/lib/FelBind/generate.rb diff --git a/src/sample.config.rb b/lib/FelBind/sample.config.rb index 91d883b..91d883b 100644 --- a/src/sample.config.rb +++ b/lib/FelBind/sample.config.rb diff --git a/src/scan.rb b/lib/FelBind/scan.rb index 9f9aede..9f9aede 100644 --- a/src/scan.rb +++ b/lib/FelBind/scan.rb diff --git a/src/templates.rb b/lib/FelBind/templates.rb index 7d88876..7d88876 100644 --- a/src/templates.rb +++ b/lib/FelBind/templates.rb diff --git a/lib/FelBind/version.rb b/lib/FelBind/version.rb new file mode 100644 index 0000000..48c42e3 --- /dev/null +++ b/lib/FelBind/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module FelBind + VERSION = "0.1.0" +end diff --git a/planning/structure.png b/planning/structure.png Binary files differnew file mode 100644 index 0000000..6996f20 --- /dev/null +++ b/planning/structure.png diff --git a/sig/FelBind.rbs b/sig/FelBind.rbs new file mode 100644 index 0000000..38e8ac4 --- /dev/null +++ b/sig/FelBind.rbs @@ -0,0 +1,4 @@ +module FelBind + VERSION: String + # See the writing guide of rbs: https://github.com/ruby/rbs#guides +end diff --git a/src/backend/test.h b/src/backend/test.h deleted file mode 100644 index c7b5ea9..0000000 --- a/src/backend/test.h +++ /dev/null @@ -1,12 +0,0 @@ -void InitWindow(int width, int height, const char *title); - -typedef struct Rectangle { - float height; // Rectangle height -} Rectangle; - -typedef struct Texture { - unsigned int id; // OpenGL texture id - int format; // Data format (PixelFormat type) -} Texture; - -typedef Texture Texture2D; |
