summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore11
-rw-r--r--.rubocop.yml13
-rw-r--r--.tool-versions1
-rw-r--r--CHANGELOG.md5
-rw-r--r--FelBind.gemspec39
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock35
-rw-r--r--LICENSE.txt21
-rw-r--r--README.md33
-rw-r--r--Rakefile27
-rwxr-xr-xbin/console15
-rwxr-xr-xbin/setup8
-rwxr-xr-xexe/felbind78
-rw-r--r--ideas/stuff.rb80
-rw-r--r--lib/FelBind.rb156
-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.rb502
-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.rb5
-rw-r--r--planning/structure.pngbin0 -> 212489 bytes
-rw-r--r--sig/FelBind.rbs4
-rw-r--r--src/backend/test.h12
26 files changed, 1011 insertions, 41 deletions
diff --git a/.gitignore b/.gitignore
index 2f6fcd2..e4502a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Gemfile b/Gemfile
index 527fc7f..d7e57ed 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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).
diff --git a/Rakefile b/Rakefile
index 986a40b..1924143 100644
--- a/Rakefile
+++ b/Rakefile
@@ -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
new file mode 100644
index 0000000..6996f20
--- /dev/null
+++ b/planning/structure.png
Binary files differ
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;