summaryrefslogtreecommitdiffhomepage
path: root/lib/FelBind.rb
blob: 1ca1cff433c0e7a09001b5cb3bd6ba970133b760 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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